abcjs 6.2.3 → 6.4.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/README.md +8 -0
- package/RELEASE.md +84 -1
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic.js +1775 -1034
- package/dist/abcjs-basic.js.map +1 -1
- package/dist/abcjs-plugin-min.js +2 -2
- package/index.js +3 -0
- package/package.json +1 -1
- package/plugin.js +1 -1
- package/src/api/abc_tablatures.js +48 -13
- package/src/api/tune-metrics.js +18 -0
- package/src/data/abc_tune.js +13 -2
- package/src/edit/abc_editarea.js +4 -1
- package/src/parse/abc_parse.js +2 -0
- package/src/parse/abc_parse_directive.js +23 -16
- package/src/parse/abc_parse_header.js +22 -19
- package/src/parse/abc_tokenizer.js +72 -7
- package/src/parse/tune-builder.js +60 -1
- package/src/synth/abc_midi_flattener.js +40 -462
- package/src/synth/abc_midi_sequencer.js +25 -10
- package/src/synth/chord-track.js +562 -0
- package/src/synth/create-note-map.js +2 -1
- package/src/synth/create-synth.js +91 -42
- package/src/synth/synth-controller.js +6 -2
- package/src/tablatures/instruments/string-patterns.js +11 -0
- package/src/tablatures/instruments/{violin/violin-patterns.js → tab-string-patterns.js} +6 -6
- package/src/tablatures/instruments/{violin/tab-violin.js → tab-string.js} +13 -11
- package/src/tablatures/tab-absolute-elements.js +16 -7
- package/src/tablatures/tab-renderer.js +22 -9
- package/src/test/abc_parser_lint.js +14 -12
- package/src/write/creation/abstract-engraver.js +6 -2
- package/src/write/creation/add-chord.js +102 -82
- package/src/write/creation/add-text-if.js +2 -2
- package/src/write/creation/decoration.js +16 -9
- package/src/write/creation/elements/bottom-text.js +62 -47
- package/src/write/creation/elements/rich-text.js +51 -0
- package/src/write/creation/elements/tie-element.js +23 -0
- package/src/write/creation/elements/top-text.js +37 -11
- package/src/write/creation/glyphs.js +1 -1
- package/src/write/draw/absolute.js +4 -1
- package/src/write/draw/draw.js +13 -4
- package/src/write/draw/non-music.js +3 -1
- package/src/write/draw/relative.js +1 -1
- package/src/write/draw/tempo.js +1 -1
- package/src/write/draw/text.js +10 -0
- package/src/write/engraver-controller.js +62 -17
- package/src/write/helpers/classes.js +1 -1
- package/src/write/helpers/get-font-and-attr.js +8 -1
- package/src/write/helpers/get-text-size.js +8 -1
- package/src/write/interactive/create-analysis.js +50 -0
- package/src/write/interactive/find-selectable-element.js +24 -0
- package/src/write/interactive/selection.js +5 -45
- package/src/write/layout/layout-in-grid.js +83 -0
- package/src/write/layout/layout.js +29 -24
- package/src/write/layout/set-upper-and-lower-elements.js +2 -0
- package/src/write/layout/staff-group.js +2 -2
- package/src/write/layout/voice-elements.js +1 -1
- package/src/write/layout/voice.js +1 -1
- package/src/write/renderer.js +3 -0
- package/src/write/svg.js +30 -0
- package/temp.txt +3 -0
- package/types/index.d.ts +142 -38
- package/version.js +1 -1
- package/.github/workflows/tests.yml +0 -29
- package/abc2xml_239/abc2xml.html +0 -769
- package/abc2xml_239/abc2xml.py +0 -2248
- package/abc2xml_239/abc2xml_changelog.html +0 -124
- package/abc2xml_239/lazy-river.abc +0 -26
- package/abc2xml_239/lazy-river.xml +0 -3698
- package/abc2xml_239/mean-to-me.abc +0 -22
- package/abc2xml_239/mean-to-me.xml +0 -2954
- package/abc2xml_239/pyparsing.py +0 -3672
- package/abc2xml_239/pyparsing.pyc +0 -0
- package/src/tablatures/instruments/guitar/guitar-patterns.js +0 -23
- package/src/tablatures/instruments/guitar/tab-guitar.js +0 -48
package/dist/abcjs-basic.js
CHANGED
|
@@ -54,6 +54,7 @@ Object.keys(tuneBook).forEach(function (key) {
|
|
|
54
54
|
abcjs[key] = tuneBook[key];
|
|
55
55
|
});
|
|
56
56
|
abcjs.renderAbc = __webpack_require__(/*! ./src/api/abc_tunebook_svg */ "./src/api/abc_tunebook_svg.js");
|
|
57
|
+
abcjs.tuneMetrics = __webpack_require__(/*! ./src/api/tune-metrics */ "./src/api/tune-metrics.js");
|
|
57
58
|
abcjs.TimingCallbacks = __webpack_require__(/*! ./src/api/abc_timing_callbacks */ "./src/api/abc_timing_callbacks.js");
|
|
58
59
|
var glyphs = __webpack_require__(/*! ./src/write/creation/glyphs */ "./src/write/creation/glyphs.js");
|
|
59
60
|
abcjs.setGlyph = glyphs.setSymbol;
|
|
@@ -69,6 +70,7 @@ var supportsAudio = __webpack_require__(/*! ./src/synth/supports-audio */ "./src
|
|
|
69
70
|
var playEvent = __webpack_require__(/*! ./src/synth/play-event */ "./src/synth/play-event.js");
|
|
70
71
|
var SynthController = __webpack_require__(/*! ./src/synth/synth-controller */ "./src/synth/synth-controller.js");
|
|
71
72
|
var getMidiFile = __webpack_require__(/*! ./src/synth/get-midi-file */ "./src/synth/get-midi-file.js");
|
|
73
|
+
var midiRenderer = __webpack_require__(/*! ./src/synth/abc_midi_renderer */ "./src/synth/abc_midi_renderer.js");
|
|
72
74
|
abcjs.synth = {
|
|
73
75
|
CreateSynth: CreateSynth,
|
|
74
76
|
instrumentIndexToName: instrumentIndexToName,
|
|
@@ -81,7 +83,8 @@ abcjs.synth = {
|
|
|
81
83
|
supportsAudio: supportsAudio,
|
|
82
84
|
playEvent: playEvent,
|
|
83
85
|
getMidiFile: getMidiFile,
|
|
84
|
-
sequence: sequence
|
|
86
|
+
sequence: sequence,
|
|
87
|
+
midiRenderer: midiRenderer
|
|
85
88
|
};
|
|
86
89
|
abcjs['Editor'] = __webpack_require__(/*! ./src/edit/abc_editor */ "./src/edit/abc_editor.js");
|
|
87
90
|
abcjs['EditArea'] = __webpack_require__(/*! ./src/edit/abc_editarea */ "./src/edit/abc_editarea.js");
|
|
@@ -203,17 +206,42 @@ module.exports = animation;
|
|
|
203
206
|
* where plugin represents a plugin instance
|
|
204
207
|
*
|
|
205
208
|
*/
|
|
206
|
-
var
|
|
207
|
-
var GuitarTablature = __webpack_require__(/*! ../tablatures/instruments/guitar/tab-guitar */ "./src/tablatures/instruments/guitar/tab-guitar.js");
|
|
209
|
+
var StringTablature = __webpack_require__(/*! ../tablatures/instruments/tab-string */ "./src/tablatures/instruments/tab-string.js");
|
|
208
210
|
|
|
209
211
|
/* extend the table below when adding a new instrument plugin */
|
|
210
212
|
|
|
211
213
|
// Existing tab classes
|
|
212
214
|
var pluginTab = {
|
|
213
|
-
'violin':
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
215
|
+
'violin': {
|
|
216
|
+
name: 'StringTab',
|
|
217
|
+
defaultTuning: ['G,', 'D', 'A', 'e'],
|
|
218
|
+
isTabBig: false,
|
|
219
|
+
tabSymbolOffset: 0
|
|
220
|
+
},
|
|
221
|
+
'fiddle': {
|
|
222
|
+
name: 'StringTab',
|
|
223
|
+
defaultTuning: ['G,', 'D', 'A', 'e'],
|
|
224
|
+
isTabBig: false,
|
|
225
|
+
tabSymbolOffset: 0
|
|
226
|
+
},
|
|
227
|
+
'mandolin': {
|
|
228
|
+
name: 'StringTab',
|
|
229
|
+
defaultTuning: ['G,', 'D', 'A', 'e'],
|
|
230
|
+
isTabBig: false,
|
|
231
|
+
tabSymbolOffset: 0
|
|
232
|
+
},
|
|
233
|
+
'guitar': {
|
|
234
|
+
name: 'StringTab',
|
|
235
|
+
defaultTuning: ['E,', 'A,', 'D', 'G', 'B', 'e'],
|
|
236
|
+
isTabBig: true,
|
|
237
|
+
tabSymbolOffset: 0
|
|
238
|
+
},
|
|
239
|
+
'fiveString': {
|
|
240
|
+
name: 'StringTab',
|
|
241
|
+
defaultTuning: ['C,', 'G,', 'D', 'A', 'e'],
|
|
242
|
+
isTabBig: false,
|
|
243
|
+
tabSymbolOffset: -.95
|
|
244
|
+
}
|
|
217
245
|
};
|
|
218
246
|
var abcTablatures = {
|
|
219
247
|
inited: false,
|
|
@@ -258,7 +286,7 @@ var abcTablatures = {
|
|
|
258
286
|
var tabName = pluginTab[instrument];
|
|
259
287
|
var plugin = null;
|
|
260
288
|
if (tabName) {
|
|
261
|
-
plugin = this.plugins[tabName];
|
|
289
|
+
plugin = this.plugins[tabName.name];
|
|
262
290
|
}
|
|
263
291
|
if (plugin) {
|
|
264
292
|
if (params.visualTranspose != 0) {
|
|
@@ -270,7 +298,8 @@ var abcTablatures = {
|
|
|
270
298
|
classz: plugin,
|
|
271
299
|
tuneNumber: tuneNumber,
|
|
272
300
|
params: args,
|
|
273
|
-
instance: null
|
|
301
|
+
instance: null,
|
|
302
|
+
tabType: tabName
|
|
274
303
|
};
|
|
275
304
|
// proceed with tab plugin init
|
|
276
305
|
// plugin.init(tune, tuneNumber, args, ii);
|
|
@@ -296,20 +325,50 @@ var abcTablatures = {
|
|
|
296
325
|
*/
|
|
297
326
|
layoutTablatures: function layoutTablatures(renderer, abcTune) {
|
|
298
327
|
var tabs = abcTune.tablatures;
|
|
328
|
+
|
|
299
329
|
// chack tabs request for each staffs
|
|
330
|
+
var staffLineCount = 0;
|
|
331
|
+
|
|
332
|
+
// Clear the suppression flag
|
|
333
|
+
if (tabs && tabs.length > 0) {
|
|
334
|
+
var nTabs = tabs.length;
|
|
335
|
+
for (var kk = 0; kk < nTabs; ++kk) {
|
|
336
|
+
if (tabs[kk] && tabs[kk].params.firstStaffOnly) {
|
|
337
|
+
tabs[kk].params.suppress = false;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
300
341
|
for (var ii = 0; ii < abcTune.lines.length; ii++) {
|
|
301
342
|
var line = abcTune.lines[ii];
|
|
343
|
+
if (line.staff) {
|
|
344
|
+
staffLineCount++;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// MAE 27Nov2023
|
|
348
|
+
// If tab param "firstStaffOnly", remove the tab label after the first staff
|
|
349
|
+
if (staffLineCount > 1) {
|
|
350
|
+
if (tabs && tabs.length > 0) {
|
|
351
|
+
var nTabs = tabs.length;
|
|
352
|
+
for (var kk = 0; kk < nTabs; ++kk) {
|
|
353
|
+
if (tabs[kk].params.firstStaffOnly) {
|
|
354
|
+
// Set the staff draw suppression flag
|
|
355
|
+
tabs[kk].params.suppress = true;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
302
360
|
var curStaff = line.staff;
|
|
303
361
|
if (curStaff) {
|
|
362
|
+
var maxStaves = curStaff.length;
|
|
304
363
|
for (var jj = 0; jj < curStaff.length; jj++) {
|
|
305
|
-
if (tabs[jj]) {
|
|
364
|
+
if (tabs[jj] && jj < maxStaves) {
|
|
306
365
|
// tablature requested for staff
|
|
307
366
|
var tabPlugin = tabs[jj];
|
|
308
367
|
if (tabPlugin.instance == null) {
|
|
309
368
|
tabPlugin.instance = new tabPlugin.classz();
|
|
310
369
|
// plugin.init(tune, tuneNumber, args, ii);
|
|
311
370
|
// call initer first
|
|
312
|
-
tabPlugin.instance.init(abcTune, tabPlugin.tuneNumber, tabPlugin.params, jj);
|
|
371
|
+
tabPlugin.instance.init(abcTune, tabPlugin.tuneNumber, tabPlugin.params, jj, tabPlugin.tabType);
|
|
313
372
|
}
|
|
314
373
|
// render next
|
|
315
374
|
tabPlugin.instance.render(renderer, line, jj);
|
|
@@ -324,8 +383,7 @@ var abcTablatures = {
|
|
|
324
383
|
init: function init() {
|
|
325
384
|
// just register plugin hosted by abcjs
|
|
326
385
|
if (!this.inited) {
|
|
327
|
-
this.register(new
|
|
328
|
-
this.register(new GuitarTablature());
|
|
386
|
+
this.register(new StringTablature());
|
|
329
387
|
this.inited = true;
|
|
330
388
|
}
|
|
331
389
|
}
|
|
@@ -1031,6 +1089,32 @@ module.exports = renderAbc;
|
|
|
1031
1089
|
|
|
1032
1090
|
/***/ }),
|
|
1033
1091
|
|
|
1092
|
+
/***/ "./src/api/tune-metrics.js":
|
|
1093
|
+
/*!*********************************!*\
|
|
1094
|
+
!*** ./src/api/tune-metrics.js ***!
|
|
1095
|
+
\*********************************/
|
|
1096
|
+
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
1097
|
+
|
|
1098
|
+
var tunebook = __webpack_require__(/*! ./abc_tunebook */ "./src/api/abc_tunebook.js");
|
|
1099
|
+
var EngraverController = __webpack_require__(/*! ../write/engraver-controller */ "./src/write/engraver-controller.js");
|
|
1100
|
+
var tuneMetrics = function tuneMetrics(abc, params) {
|
|
1101
|
+
function callback(div, tune, tuneNumber, abcString) {
|
|
1102
|
+
div = document.createElement("div");
|
|
1103
|
+
div.setAttribute("style", "visibility: hidden;");
|
|
1104
|
+
document.body.appendChild(div);
|
|
1105
|
+
var engraver_controller = new EngraverController(div, params);
|
|
1106
|
+
var widths = engraver_controller.getMeasureWidths(tune);
|
|
1107
|
+
div.parentNode.removeChild(div);
|
|
1108
|
+
return {
|
|
1109
|
+
sections: widths
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
return tunebook.renderEngine(callback, "*", abc, params);
|
|
1113
|
+
};
|
|
1114
|
+
module.exports = tuneMetrics;
|
|
1115
|
+
|
|
1116
|
+
/***/ }),
|
|
1117
|
+
|
|
1034
1118
|
/***/ "./src/const/key-accidentals.js":
|
|
1035
1119
|
/*!**************************************!*\
|
|
1036
1120
|
!*** ./src/const/key-accidentals.js ***!
|
|
@@ -1609,9 +1693,12 @@ var Tune = function Tune() {
|
|
|
1609
1693
|
eventHash["event" + voiceTimeMilliseconds].measureStart = true;
|
|
1610
1694
|
nextIsBar = false;
|
|
1611
1695
|
}
|
|
1612
|
-
|
|
1696
|
+
// TODO-PER: There doesn't seem to be a harm in letting ties be two different notes and it fixes a bug when a tie goes to a new line. If there aren't other problems with this change, then the variable can be removed completely.
|
|
1697
|
+
// if (isTiedToNext)
|
|
1698
|
+
// isTiedState = voiceTimeMilliseconds;
|
|
1613
1699
|
}
|
|
1614
1700
|
}
|
|
1701
|
+
|
|
1615
1702
|
return {
|
|
1616
1703
|
isTiedState: isTiedState,
|
|
1617
1704
|
duration: realDuration / timeDivider,
|
|
@@ -1839,6 +1926,14 @@ var Tune = function Tune() {
|
|
|
1839
1926
|
this.deline = function (options) {
|
|
1840
1927
|
return delineTune(this.lines, options);
|
|
1841
1928
|
};
|
|
1929
|
+
this.findSelectableElement = function (target) {
|
|
1930
|
+
if (this.engraver && this.engraver.selectables) return this.engraver.findSelectableElement(target);
|
|
1931
|
+
return null;
|
|
1932
|
+
};
|
|
1933
|
+
this.getSelectableArray = function () {
|
|
1934
|
+
if (this.engraver && this.engraver.selectables) return this.engraver.selectables;
|
|
1935
|
+
return [];
|
|
1936
|
+
};
|
|
1842
1937
|
};
|
|
1843
1938
|
module.exports = Tune;
|
|
1844
1939
|
|
|
@@ -2094,7 +2189,7 @@ try {
|
|
|
2094
2189
|
// if we aren't in a browser, this code will crash, but it is not needed then either.
|
|
2095
2190
|
}
|
|
2096
2191
|
var EditArea = function EditArea(textareaid) {
|
|
2097
|
-
this.textarea = document.getElementById(textareaid);
|
|
2192
|
+
if (typeof textareaid === "string") this.textarea = document.getElementById(textareaid);else this.textarea = textareaid;
|
|
2098
2193
|
this.initialText = this.textarea.value;
|
|
2099
2194
|
this.isDragging = false;
|
|
2100
2195
|
};
|
|
@@ -2722,7 +2817,9 @@ var Parse = function Parse() {
|
|
|
2722
2817
|
setupEvents: tune.setupEvents,
|
|
2723
2818
|
setTiming: tune.setTiming,
|
|
2724
2819
|
setUpAudio: tune.setUpAudio,
|
|
2725
|
-
deline: tune.deline
|
|
2820
|
+
deline: tune.deline,
|
|
2821
|
+
findSelectableElement: tune.findSelectableElement,
|
|
2822
|
+
getSelectableArray: tune.getSelectableArray
|
|
2726
2823
|
};
|
|
2727
2824
|
if (tune.lineBreaks) t.lineBreaks = tune.lineBreaks;
|
|
2728
2825
|
if (tune.visualTranspose) t.visualTranspose = tune.visualTranspose;
|
|
@@ -4169,31 +4266,30 @@ var parseDirective = {};
|
|
|
4169
4266
|
}
|
|
4170
4267
|
};
|
|
4171
4268
|
parseDirective.parseFontChangeLine = function (textstr) {
|
|
4269
|
+
// We don't want to match two dollar signs, so change those temporarily
|
|
4270
|
+
textstr = textstr.replace(/\$\$/g, "\x03");
|
|
4172
4271
|
var textParts = textstr.split('$');
|
|
4173
4272
|
if (textParts.length > 1 && multilineVars.setfont) {
|
|
4174
|
-
var textarr = [
|
|
4175
|
-
|
|
4176
|
-
|
|
4273
|
+
var textarr = [];
|
|
4274
|
+
if (textParts[0] !== '')
|
|
4275
|
+
// did the original string start with `$`?
|
|
4276
|
+
textarr.push({
|
|
4277
|
+
text: textParts[0]
|
|
4278
|
+
});
|
|
4177
4279
|
for (var i = 1; i < textParts.length; i++) {
|
|
4178
4280
|
if (textParts[i][0] === '0') textarr.push({
|
|
4179
|
-
text: textParts[i].substring(1)
|
|
4180
|
-
});else
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
text
|
|
4186
|
-
}
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
text: textParts[i].substring(1)
|
|
4192
|
-
});else textarr[textarr.length - 1].text += '$' + textParts[i];
|
|
4193
|
-
}
|
|
4194
|
-
if (textarr.length > 1) return textarr;
|
|
4195
|
-
}
|
|
4196
|
-
return textstr;
|
|
4281
|
+
text: textParts[i].substring(1).replace(/\x03/g, "$$")
|
|
4282
|
+
});else {
|
|
4283
|
+
var whichFont = parseInt(textParts[i][0], 10);
|
|
4284
|
+
if (multilineVars.setfont[whichFont]) textarr.push({
|
|
4285
|
+
font: multilineVars.setfont[whichFont],
|
|
4286
|
+
text: textParts[i].substring(1).replace(/\x03/g, "$$")
|
|
4287
|
+
});else textarr[textarr.length - 1].text += '$' + textParts[i].replace(/\x03/g, "$$");
|
|
4288
|
+
}
|
|
4289
|
+
}
|
|
4290
|
+
return textarr;
|
|
4291
|
+
}
|
|
4292
|
+
return textstr.replace(/\x03/g, "$$");
|
|
4197
4293
|
};
|
|
4198
4294
|
var positionChoices = ['auto', 'above', 'below', 'hidden'];
|
|
4199
4295
|
parseDirective.addDirective = function (str) {
|
|
@@ -4241,6 +4337,9 @@ var parseDirective = {};
|
|
|
4241
4337
|
case "jazzchords":
|
|
4242
4338
|
tune.formatting.jazzchords = true;
|
|
4243
4339
|
break;
|
|
4340
|
+
case "accentAbove":
|
|
4341
|
+
tune.formatting.accentAbove = true;
|
|
4342
|
+
break;
|
|
4244
4343
|
case "germanAlphabet":
|
|
4245
4344
|
tune.formatting.germanAlphabet = true;
|
|
4246
4345
|
break;
|
|
@@ -4294,6 +4393,7 @@ var parseDirective = {};
|
|
|
4294
4393
|
case "pageheight":
|
|
4295
4394
|
case "pagewidth":
|
|
4296
4395
|
case "rightmargin":
|
|
4396
|
+
case "stafftopmargin":
|
|
4297
4397
|
case "staffsep":
|
|
4298
4398
|
case "staffwidth":
|
|
4299
4399
|
case "subtitlespace":
|
|
@@ -4432,7 +4532,7 @@ var parseDirective = {};
|
|
|
4432
4532
|
if (sfTokens.length >= 4) {
|
|
4433
4533
|
if (sfTokens[0].token === '-' && sfTokens[1].type === 'number') {
|
|
4434
4534
|
var sfNum = parseInt(sfTokens[1].token);
|
|
4435
|
-
if (sfNum >= 1 && sfNum <=
|
|
4535
|
+
if (sfNum >= 1 && sfNum <= 9) {
|
|
4436
4536
|
if (!multilineVars.setfont) multilineVars.setfont = [];
|
|
4437
4537
|
sfTokens.shift();
|
|
4438
4538
|
sfTokens.shift();
|
|
@@ -4694,6 +4794,10 @@ var parseDirective = {};
|
|
|
4694
4794
|
if (tokens.length !== 1 || tokens[0].type !== 'number') warn("Directive \"" + cmd + "\" requires a number as a parameter.");
|
|
4695
4795
|
tune.formatting.fontboxpadding = tokens[0].floatt;
|
|
4696
4796
|
break;
|
|
4797
|
+
case "stafftopmargin":
|
|
4798
|
+
if (tokens.length !== 1 || tokens[0].type !== 'number') warn("Directive \"" + cmd + "\" requires a number as a parameter.");
|
|
4799
|
+
tune.formatting.stafftopmargin = tokens[0].floatt;
|
|
4800
|
+
break;
|
|
4697
4801
|
case "stretchlast":
|
|
4698
4802
|
var sl = parseStretchLast(tokens);
|
|
4699
4803
|
if (sl.value !== undefined) tune.formatting.stretchlast = sl.value;
|
|
@@ -4750,17 +4854,15 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
|
|
|
4750
4854
|
parseDirective.initialize(tokenizer, warn, multilineVars, tune, tuneBuilder);
|
|
4751
4855
|
};
|
|
4752
4856
|
this.reset(tokenizer, warn, multilineVars, tune);
|
|
4753
|
-
this.setTitle = function (title) {
|
|
4754
|
-
if (multilineVars.hasMainTitle) tuneBuilder.addSubtitle(
|
|
4857
|
+
this.setTitle = function (title, origSize) {
|
|
4858
|
+
if (multilineVars.hasMainTitle) tuneBuilder.addSubtitle(title, {
|
|
4755
4859
|
startChar: multilineVars.iChar,
|
|
4756
|
-
endChar: multilineVars.iChar +
|
|
4860
|
+
endChar: multilineVars.iChar + origSize + 2
|
|
4757
4861
|
}); // display secondary title
|
|
4758
4862
|
else {
|
|
4759
|
-
|
|
4760
|
-
if (multilineVars.titlecaps) titleStr = titleStr.toUpperCase();
|
|
4761
|
-
tuneBuilder.addMetaText("title", titleStr, {
|
|
4863
|
+
tuneBuilder.addMetaText("title", title, {
|
|
4762
4864
|
startChar: multilineVars.iChar,
|
|
4763
|
-
endChar: multilineVars.iChar +
|
|
4865
|
+
endChar: multilineVars.iChar + origSize + 2
|
|
4764
4866
|
});
|
|
4765
4867
|
multilineVars.hasMainTitle = true;
|
|
4766
4868
|
}
|
|
@@ -5120,12 +5222,13 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
|
|
|
5120
5222
|
if (result.foundKey && tuneBuilder.hasBeginMusic()) tuneBuilder.appendStartingElement('key', startChar, endChar, parseKeyVoice.fixKey(multilineVars.clef, multilineVars.key));
|
|
5121
5223
|
return [e - i + 1 + ws];
|
|
5122
5224
|
case "[P:":
|
|
5225
|
+
var part = parseDirective.parseFontChangeLine(line.substring(i + 3, e));
|
|
5123
5226
|
if (startLine || tune.lines.length <= tune.lineNum) multilineVars.partForNextLine = {
|
|
5124
|
-
title:
|
|
5227
|
+
title: part,
|
|
5125
5228
|
startChar: startChar,
|
|
5126
5229
|
endChar: endChar
|
|
5127
5230
|
};else tuneBuilder.appendElement('part', startChar, endChar, {
|
|
5128
|
-
title:
|
|
5231
|
+
title: part
|
|
5129
5232
|
});
|
|
5130
5233
|
return [e - i + 1 + ws];
|
|
5131
5234
|
case "[L:":
|
|
@@ -5216,28 +5319,34 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
|
|
|
5216
5319
|
};
|
|
5217
5320
|
this.parseHeader = function (line) {
|
|
5218
5321
|
var field = metaTextHeaders[line[0]];
|
|
5219
|
-
|
|
5220
|
-
|
|
5322
|
+
var origSize = line.length - 2;
|
|
5323
|
+
var restOfLine = tokenizer.translateString(tokenizer.stripComment(line.substring(2)));
|
|
5324
|
+
if (field === 'unalignedWords' || field === 'notes') {
|
|
5325
|
+
// These fields can be multi-line
|
|
5326
|
+
tuneBuilder.addMetaTextArray(field, parseDirective.parseFontChangeLine(restOfLine), {
|
|
5221
5327
|
startChar: multilineVars.iChar,
|
|
5222
5328
|
endChar: multilineVars.iChar + line.length
|
|
5223
|
-
});
|
|
5329
|
+
});
|
|
5330
|
+
} else if (field !== undefined) {
|
|
5331
|
+
// these fields are single line
|
|
5332
|
+
tuneBuilder.addMetaText(field, parseDirective.parseFontChangeLine(restOfLine), {
|
|
5224
5333
|
startChar: multilineVars.iChar,
|
|
5225
5334
|
endChar: multilineVars.iChar + line.length
|
|
5226
5335
|
});
|
|
5227
|
-
return {};
|
|
5228
5336
|
} else {
|
|
5229
5337
|
var startChar = multilineVars.iChar;
|
|
5230
5338
|
var endChar = startChar + line.length;
|
|
5231
5339
|
switch (line[0]) {
|
|
5232
5340
|
case 'H':
|
|
5233
|
-
|
|
5341
|
+
// History is a little different because once it starts it continues until another header field is encountered
|
|
5342
|
+
tuneBuilder.addMetaTextArray("history", parseDirective.parseFontChangeLine(restOfLine), {
|
|
5234
5343
|
startChar: multilineVars.iChar,
|
|
5235
5344
|
endChar: multilineVars.iChar + line.length
|
|
5236
5345
|
});
|
|
5237
5346
|
line = tokenizer.peekLine();
|
|
5238
5347
|
while (line && line[1] !== ':') {
|
|
5239
5348
|
tokenizer.nextLine();
|
|
5240
|
-
tuneBuilder.
|
|
5349
|
+
tuneBuilder.addMetaTextArray("history", parseDirective.parseFontChangeLine(tokenizer.translateString(tokenizer.stripComment(line))), {
|
|
5241
5350
|
startChar: multilineVars.iChar,
|
|
5242
5351
|
endChar: multilineVars.iChar + line.length
|
|
5243
5352
|
});
|
|
@@ -5262,11 +5371,11 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
|
|
|
5262
5371
|
break;
|
|
5263
5372
|
case 'P':
|
|
5264
5373
|
// TODO-PER: There is more to do with parts, but the writer doesn't care.
|
|
5265
|
-
if (multilineVars.is_in_header) tuneBuilder.addMetaText("partOrder",
|
|
5374
|
+
if (multilineVars.is_in_header) tuneBuilder.addMetaText("partOrder", parseDirective.parseFontChangeLine(restOfLine), {
|
|
5266
5375
|
startChar: multilineVars.iChar,
|
|
5267
5376
|
endChar: multilineVars.iChar + line.length
|
|
5268
5377
|
});else multilineVars.partForNextLine = {
|
|
5269
|
-
title:
|
|
5378
|
+
title: restOfLine,
|
|
5270
5379
|
startChar: startChar,
|
|
5271
5380
|
endChar: endChar
|
|
5272
5381
|
};
|
|
@@ -5278,7 +5387,8 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
|
|
|
5278
5387
|
}
|
|
5279
5388
|
break;
|
|
5280
5389
|
case 'T':
|
|
5281
|
-
|
|
5390
|
+
if (multilineVars.titlecaps) restOfLine = restOfLine.toUpperCase();
|
|
5391
|
+
this.setTitle(parseDirective.parseFontChangeLine(tokenizer.theReverser(restOfLine)), origSize);
|
|
5282
5392
|
break;
|
|
5283
5393
|
case 'U':
|
|
5284
5394
|
this.addUserDefinition(line, 2, line.length);
|
|
@@ -8718,9 +8828,67 @@ var Tokenizer = function Tokenizer(lines, multilineVars) {
|
|
|
8718
8828
|
index: index
|
|
8719
8829
|
};
|
|
8720
8830
|
};
|
|
8831
|
+
|
|
8832
|
+
//
|
|
8833
|
+
// MAE 10 Jan 2023 - For better handling of tunes that have tune numbers in front of them.
|
|
8834
|
+
//
|
|
8835
|
+
// Previous version would take:
|
|
8836
|
+
// 21. Woman of the House, The
|
|
8837
|
+
// and return:
|
|
8838
|
+
// The 21. Woman of the House
|
|
8839
|
+
//
|
|
8840
|
+
// This fix results in:
|
|
8841
|
+
// 21. The Woman of the House
|
|
8842
|
+
//
|
|
8843
|
+
// Also added additional checks and handlers for lower case ", the" and ", a" since I found several tune collections with those tune name constructs
|
|
8844
|
+
//
|
|
8845
|
+
// Find an optional title number at the start of a tune title
|
|
8846
|
+
function getTitleNumber(str) {
|
|
8847
|
+
var regex = /^(\d+)\./;
|
|
8848
|
+
|
|
8849
|
+
// Use the exec method to search for the pattern in the string
|
|
8850
|
+
var match = regex.exec(str);
|
|
8851
|
+
|
|
8852
|
+
// Check if a match is found
|
|
8853
|
+
if (match) {
|
|
8854
|
+
// The matched number is captured in the first group (index 1)
|
|
8855
|
+
var foundNumber = match[1];
|
|
8856
|
+
return foundNumber;
|
|
8857
|
+
} else {
|
|
8858
|
+
// Return null if no match is found
|
|
8859
|
+
return null;
|
|
8860
|
+
}
|
|
8861
|
+
}
|
|
8862
|
+
var thePatterns = [{
|
|
8863
|
+
match: /,\s*[Tt]he$/,
|
|
8864
|
+
replace: "The "
|
|
8865
|
+
}, {
|
|
8866
|
+
match: /,\s*[Aa]$/,
|
|
8867
|
+
replace: "A "
|
|
8868
|
+
}, {
|
|
8869
|
+
match: /,\s*[Aa]n$/,
|
|
8870
|
+
replace: "An "
|
|
8871
|
+
}];
|
|
8721
8872
|
this.theReverser = function (str) {
|
|
8722
|
-
|
|
8723
|
-
|
|
8873
|
+
for (var i = 0; i < thePatterns.length; i++) {
|
|
8874
|
+
var thisPattern = thePatterns[i];
|
|
8875
|
+
var match = str.match(thisPattern.match);
|
|
8876
|
+
if (match) {
|
|
8877
|
+
var theTitleNumber = getTitleNumber(str);
|
|
8878
|
+
if (theTitleNumber) {
|
|
8879
|
+
//console.log("theReverser The titlenumber:"+theTitleNumber);
|
|
8880
|
+
|
|
8881
|
+
str = str.replace(theTitleNumber + ".", "");
|
|
8882
|
+
str = str.trim();
|
|
8883
|
+
}
|
|
8884
|
+
var len = match[0].length;
|
|
8885
|
+
var result = thisPattern.replace + str.substring(0, str.length - len);
|
|
8886
|
+
if (theTitleNumber) {
|
|
8887
|
+
result = theTitleNumber + ". " + result;
|
|
8888
|
+
}
|
|
8889
|
+
return result;
|
|
8890
|
+
}
|
|
8891
|
+
}
|
|
8724
8892
|
return str;
|
|
8725
8893
|
};
|
|
8726
8894
|
this.stripComment = function (str) {
|
|
@@ -9154,6 +9322,7 @@ module.exports = transposeChordName;
|
|
|
9154
9322
|
|
|
9155
9323
|
var parseKeyVoice = __webpack_require__(/*! ../parse/abc_parse_key_voice */ "./src/parse/abc_parse_key_voice.js");
|
|
9156
9324
|
var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/abc_common.js");
|
|
9325
|
+
var parseDirective = __webpack_require__(/*! ./abc_parse_directive */ "./src/parse/abc_parse_directive.js");
|
|
9157
9326
|
var TuneBuilder = function TuneBuilder(tune) {
|
|
9158
9327
|
var self = this;
|
|
9159
9328
|
this.setVisualTranspose = function (visualTranspose) {
|
|
@@ -9313,6 +9482,8 @@ var TuneBuilder = function TuneBuilder(tune) {
|
|
|
9313
9482
|
this.cleanUp = function (barsperstaff, staffnonote, currSlur) {
|
|
9314
9483
|
this.closeLine(); // Close the last line.
|
|
9315
9484
|
delete tune.runningFonts;
|
|
9485
|
+
simplifyMetaText(tune);
|
|
9486
|
+
//addRichTextToAnnotationsAndLyrics(tune)
|
|
9316
9487
|
|
|
9317
9488
|
// If the tempo was created with a string like "Allegro", then the duration of a beat needs to be set at the last moment, when it is most likely known.
|
|
9318
9489
|
if (tune.metaText.tempo && tune.metaText.tempo.bpm && !tune.metaText.tempo.duration) tune.metaText.tempo.duration = [tune.getBeatLength()];
|
|
@@ -10047,7 +10218,15 @@ var TuneBuilder = function TuneBuilder(tune) {
|
|
|
10047
10218
|
tune.metaText[key] = value;
|
|
10048
10219
|
tune.metaTextInfo[key] = info;
|
|
10049
10220
|
} else {
|
|
10050
|
-
tune.metaText[key] += "\n" + value;
|
|
10221
|
+
if (typeof tune.metaText[key] === 'string' && typeof value === 'string') tune.metaText[key] += "\n" + value;else {
|
|
10222
|
+
if (tune.metaText[key] === 'string') tune.metaText[key] = [{
|
|
10223
|
+
text: tune.metaText[key]
|
|
10224
|
+
}];
|
|
10225
|
+
if (typeof value === 'string') value = [{
|
|
10226
|
+
text: value
|
|
10227
|
+
}];
|
|
10228
|
+
tune.metaText[key] = tune.metaText[key].concat(value);
|
|
10229
|
+
}
|
|
10051
10230
|
tune.metaTextInfo[key].endChar = info.endChar;
|
|
10052
10231
|
}
|
|
10053
10232
|
};
|
|
@@ -10065,6 +10244,46 @@ var TuneBuilder = function TuneBuilder(tune) {
|
|
|
10065
10244
|
tune.metaTextInfo[key] = info;
|
|
10066
10245
|
};
|
|
10067
10246
|
};
|
|
10247
|
+
function isArrayOfStrings(arr) {
|
|
10248
|
+
if (!arr) return false;
|
|
10249
|
+
if (typeof arr === "string") return false;
|
|
10250
|
+
var str = '';
|
|
10251
|
+
for (var i = 0; i < arr.length; i++) {
|
|
10252
|
+
if (typeof arr[i] !== 'string') return false;
|
|
10253
|
+
}
|
|
10254
|
+
return true;
|
|
10255
|
+
}
|
|
10256
|
+
function simplifyMetaText(tune) {
|
|
10257
|
+
if (isArrayOfStrings(tune.metaText.notes)) tune.metaText.notes = tune.metaText.notes.join("\n");
|
|
10258
|
+
if (isArrayOfStrings(tune.metaText.history)) tune.metaText.history = tune.metaText.history.join("\n");
|
|
10259
|
+
}
|
|
10260
|
+
function addRichTextToAnnotationsAndLyrics(tune) {
|
|
10261
|
+
var lines = tune.lines;
|
|
10262
|
+
for (var i = 0; i < lines.length; i++) {
|
|
10263
|
+
if (lines[i].staff !== undefined) {
|
|
10264
|
+
for (var s = 0; s < lines[i].staff.length; s++) {
|
|
10265
|
+
for (var v = 0; v < lines[i].staff[s].voices.length; v++) {
|
|
10266
|
+
var voice = lines[i].staff[s].voices[v];
|
|
10267
|
+
for (var n = 0; n < voice.length; n++) {
|
|
10268
|
+
var element = voice[n];
|
|
10269
|
+
if (element.chord) {
|
|
10270
|
+
for (var c = 0; c < element.chord.length; c++) {
|
|
10271
|
+
element.chord[c].name = parseDirective.parseFontChangeLine(element.chord[c].name);
|
|
10272
|
+
console.log(element.chord[c].name);
|
|
10273
|
+
}
|
|
10274
|
+
}
|
|
10275
|
+
if (element.lyric) {
|
|
10276
|
+
for (var l = 0; l < element.lyric.length; l++) {
|
|
10277
|
+
element.lyric[l].syllable = parseDirective.parseFontChangeLine(element.lyric[l].syllable);
|
|
10278
|
+
console.log(element.lyric[l].syllable);
|
|
10279
|
+
}
|
|
10280
|
+
}
|
|
10281
|
+
}
|
|
10282
|
+
}
|
|
10283
|
+
}
|
|
10284
|
+
}
|
|
10285
|
+
}
|
|
10286
|
+
}
|
|
10068
10287
|
module.exports = TuneBuilder;
|
|
10069
10288
|
|
|
10070
10289
|
/***/ }),
|
|
@@ -11035,7 +11254,7 @@ module.exports = strTranspose;
|
|
|
11035
11254
|
// It also extracts guitar chords to a separate voice and resolves their rhythm.
|
|
11036
11255
|
|
|
11037
11256
|
var flatten;
|
|
11038
|
-
var
|
|
11257
|
+
var ChordTrack = __webpack_require__(/*! ./chord-track */ "./src/synth/chord-track.js");
|
|
11039
11258
|
var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pitches-to-perc.js");
|
|
11040
11259
|
(function () {
|
|
11041
11260
|
"use strict";
|
|
@@ -11055,25 +11274,13 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11055
11274
|
var lastNoteDurationPosition;
|
|
11056
11275
|
var currentTrackName;
|
|
11057
11276
|
var lastEventTime;
|
|
11277
|
+
var chordTrack;
|
|
11058
11278
|
var meter = {
|
|
11059
11279
|
num: 4,
|
|
11060
11280
|
den: 4
|
|
11061
11281
|
};
|
|
11062
|
-
var chordTrack;
|
|
11063
|
-
var chordSourceTrack;
|
|
11064
|
-
var chordTrackFinished;
|
|
11065
|
-
var chordChannel;
|
|
11066
|
-
var bassInstrument = 0;
|
|
11067
|
-
var chordInstrument = 0;
|
|
11068
11282
|
var drumInstrument = 128;
|
|
11069
|
-
var boomVolume = 64;
|
|
11070
|
-
var chickVolume = 48;
|
|
11071
|
-
var currentChords;
|
|
11072
|
-
var lastChord;
|
|
11073
|
-
var chordLastBar;
|
|
11074
11283
|
var lastBarTime;
|
|
11075
|
-
var gChordTacet = false;
|
|
11076
|
-
var hasRhythmHead = false;
|
|
11077
11284
|
var doBeatAccents = true;
|
|
11078
11285
|
var stressBeat1 = 105;
|
|
11079
11286
|
var stressBeatDown = 95;
|
|
@@ -11111,25 +11318,10 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11111
11318
|
currentTrackName = undefined;
|
|
11112
11319
|
lastEventTime = 0;
|
|
11113
11320
|
percmap = percmap_;
|
|
11114
|
-
|
|
11115
|
-
// For resolving chords.
|
|
11116
11321
|
meter = {
|
|
11117
11322
|
num: 4,
|
|
11118
11323
|
den: 4
|
|
11119
11324
|
};
|
|
11120
|
-
chordTrack = [];
|
|
11121
|
-
chordSourceTrack = false;
|
|
11122
|
-
chordChannel = voices.length; // first free channel for chords
|
|
11123
|
-
chordTrackFinished = false;
|
|
11124
|
-
currentChords = [];
|
|
11125
|
-
bassInstrument = midiOptions.bassprog && midiOptions.bassprog.length === 1 ? midiOptions.bassprog[0] : 0;
|
|
11126
|
-
chordInstrument = midiOptions.chordprog && midiOptions.chordprog.length === 1 ? midiOptions.chordprog[0] : 0;
|
|
11127
|
-
boomVolume = midiOptions.bassvol && midiOptions.bassvol.length === 1 ? midiOptions.bassvol[0] : 64;
|
|
11128
|
-
chickVolume = midiOptions.chordvol && midiOptions.chordvol.length === 1 ? midiOptions.chordvol[0] : 48;
|
|
11129
|
-
lastChord = undefined;
|
|
11130
|
-
chordLastBar = undefined;
|
|
11131
|
-
gChordTacet = options.chordsOff ? true : false;
|
|
11132
|
-
hasRhythmHead = false;
|
|
11133
11325
|
doBeatAccents = true;
|
|
11134
11326
|
stressBeat1 = 105;
|
|
11135
11327
|
stressBeatDown = 95;
|
|
@@ -11146,10 +11338,19 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11146
11338
|
drumBars = 1;
|
|
11147
11339
|
if (voices.length > 0 && voices[0].length > 0) pickupLength = voices[0][0].pickupLength;
|
|
11148
11340
|
|
|
11341
|
+
// For resolving chords.
|
|
11342
|
+
if (options.bassprog !== undefined && !midiOptions.bassprog) midiOptions.bassprog = [options.bassprog];
|
|
11343
|
+
if (options.bassvol !== undefined && !midiOptions.bassvol) midiOptions.bassvol = [options.bassvol];
|
|
11344
|
+
if (options.chordprog !== undefined && !midiOptions.chordprog) midiOptions.chordprog = [options.chordprog];
|
|
11345
|
+
if (options.chordvol !== undefined && !midiOptions.chordvol) midiOptions.chordvol = [options.chordvol];
|
|
11346
|
+
if (options.gchord !== undefined && !midiOptions.gchord) midiOptions.gchord = [options.gchord];
|
|
11347
|
+
chordTrack = new ChordTrack(voices.length, options.chordsOff, midiOptions, meter);
|
|
11348
|
+
|
|
11149
11349
|
// First adjust the input to resolve ties, set the starting time for each note, etc. That will make the rest of the logic easier
|
|
11150
11350
|
preProcess(voices, options);
|
|
11151
11351
|
for (var i = 0; i < voices.length; i++) {
|
|
11152
11352
|
transpose = 0;
|
|
11353
|
+
chordTrack.setTranspose(transpose);
|
|
11153
11354
|
lastNoteDurationPosition = -1;
|
|
11154
11355
|
var voice = voices[i];
|
|
11155
11356
|
currentTrack = [{
|
|
@@ -11159,6 +11360,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11159
11360
|
}];
|
|
11160
11361
|
currentTrackName = undefined;
|
|
11161
11362
|
lastBarTime = 0;
|
|
11363
|
+
chordTrack.setLastBarTime(0);
|
|
11162
11364
|
var voiceOff = false;
|
|
11163
11365
|
if (options.voicesOff === true) voiceOff = true;else if (options.voicesOff && options.voicesOff.length && options.voicesOff.indexOf(i) >= 0) voiceOff = true;
|
|
11164
11366
|
for (var j = 0; j < voice.length; j++) {
|
|
@@ -11172,8 +11374,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11172
11374
|
};
|
|
11173
11375
|
break;
|
|
11174
11376
|
case "note":
|
|
11175
|
-
|
|
11176
|
-
if (setChordTrack) chordSourceTrack = i;
|
|
11377
|
+
writeNote(element, voiceOff);
|
|
11177
11378
|
break;
|
|
11178
11379
|
case "key":
|
|
11179
11380
|
accidentals = setKeySignature(element);
|
|
@@ -11181,27 +11382,27 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11181
11382
|
case "meter":
|
|
11182
11383
|
if (!startingMeter) startingMeter = element;
|
|
11183
11384
|
meter = element;
|
|
11385
|
+
chordTrack.setMeter(meter);
|
|
11184
11386
|
beatFraction = getBeatFraction(meter);
|
|
11185
11387
|
alignDrumToMeter();
|
|
11186
11388
|
break;
|
|
11187
11389
|
case "tempo":
|
|
11188
11390
|
if (!startingTempo) startingTempo = element.qpm;else tempoChangeFactor = element.qpm ? startingTempo / element.qpm : 1;
|
|
11391
|
+
chordTrack.setTempoChangeFactor(tempoChangeFactor);
|
|
11189
11392
|
break;
|
|
11190
11393
|
case "transpose":
|
|
11191
11394
|
transpose = element.transpose;
|
|
11395
|
+
chordTrack.setTranspose(transpose);
|
|
11192
11396
|
break;
|
|
11193
11397
|
case "bar":
|
|
11194
|
-
|
|
11195
|
-
resolveChords(lastBarTime, timeToRealTime(element.time));
|
|
11196
|
-
currentChords = [];
|
|
11197
|
-
}
|
|
11398
|
+
chordTrack.barEnd(element);
|
|
11198
11399
|
barAccidentals = [];
|
|
11199
11400
|
if (i === 0)
|
|
11200
11401
|
// Only write the drum part on the first voice so that it is not duplicated.
|
|
11201
11402
|
writeDrum(voices.length + 1);
|
|
11202
|
-
|
|
11203
|
-
chordLastBar = lastChord;
|
|
11403
|
+
chordTrack.setRhythmHead(false); // decide whether there are rhythm heads each measure.
|
|
11204
11404
|
lastBarTime = timeToRealTime(element.time);
|
|
11405
|
+
chordTrack.setLastBarTime(lastBarTime);
|
|
11205
11406
|
break;
|
|
11206
11407
|
case "bagpipes":
|
|
11207
11408
|
bagpipes = true;
|
|
@@ -11228,8 +11429,8 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11228
11429
|
drumDefinition = normalizeDrumDefinition(element.params);
|
|
11229
11430
|
alignDrumToMeter();
|
|
11230
11431
|
break;
|
|
11231
|
-
case "
|
|
11232
|
-
|
|
11432
|
+
case "gchordOn":
|
|
11433
|
+
chordTrack.gChordOn(element);
|
|
11233
11434
|
break;
|
|
11234
11435
|
case "beat":
|
|
11235
11436
|
stressBeat1 = element.beats[0];
|
|
@@ -11246,6 +11447,13 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11246
11447
|
case "beataccents":
|
|
11247
11448
|
doBeatAccents = element.value;
|
|
11248
11449
|
break;
|
|
11450
|
+
case "gchord":
|
|
11451
|
+
case "bassprog":
|
|
11452
|
+
case "chordprog":
|
|
11453
|
+
case "bassvol":
|
|
11454
|
+
case "chordvol":
|
|
11455
|
+
chordTrack.paramChange(element);
|
|
11456
|
+
break;
|
|
11249
11457
|
default:
|
|
11250
11458
|
// This should never happen
|
|
11251
11459
|
console.log("MIDI creation. Unknown el_type: " + element.el_type + "\n"); // jshint ignore:line
|
|
@@ -11255,16 +11463,14 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11255
11463
|
if (currentTrack[0].instrument === undefined) currentTrack[0].instrument = instrument ? instrument : 0;
|
|
11256
11464
|
if (currentTrackName) currentTrack.unshift(currentTrackName);
|
|
11257
11465
|
tracks.push(currentTrack);
|
|
11258
|
-
|
|
11259
|
-
// Don't do chords on more than one track, so turn off chord detection after we create it.
|
|
11260
|
-
chordTrackFinished = true;
|
|
11466
|
+
chordTrack.finish();
|
|
11261
11467
|
if (drumTrack.length > 0)
|
|
11262
11468
|
// Don't do drums on more than one track, so turn off drum after we create it.
|
|
11263
11469
|
drumTrackFinished = true;
|
|
11264
11470
|
}
|
|
11265
11471
|
// See if any notes are octaves played at the same time. If so, raise the pitch of the higher one.
|
|
11266
11472
|
if (options.detuneOctave) findOctaves(tracks, parseInt(options.detuneOctave, 10));
|
|
11267
|
-
|
|
11473
|
+
chordTrack.addTrack(tracks);
|
|
11268
11474
|
if (drumTrack.length > 0) tracks.push(drumTrack);
|
|
11269
11475
|
return {
|
|
11270
11476
|
tempo: startingTempo,
|
|
@@ -11281,13 +11487,6 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11281
11487
|
}
|
|
11282
11488
|
}
|
|
11283
11489
|
}
|
|
11284
|
-
function chordTrackEmpty() {
|
|
11285
|
-
var isEmpty = true;
|
|
11286
|
-
for (var i = 0; i < chordTrack.length && isEmpty; i++) {
|
|
11287
|
-
if (chordTrack[i].cmd === 'note') isEmpty = false;
|
|
11288
|
-
}
|
|
11289
|
-
return isEmpty;
|
|
11290
|
-
}
|
|
11291
11490
|
function timeToRealTime(time) {
|
|
11292
11491
|
return time / 1000000;
|
|
11293
11492
|
}
|
|
@@ -11375,48 +11574,6 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11375
11574
|
}
|
|
11376
11575
|
return 0.25;
|
|
11377
11576
|
}
|
|
11378
|
-
//
|
|
11379
|
-
// The algorithm for chords is:
|
|
11380
|
-
// - The chords are done in a separate track.
|
|
11381
|
-
// - If there are notes before the first chord, then put that much silence to start the track.
|
|
11382
|
-
// - The pattern of chord expression depends on the meter, and how many chords are in a measure.
|
|
11383
|
-
// - There is a possibility that a measure will have an incorrect number of beats, if that is the case, then
|
|
11384
|
-
// start the pattern anew on the next measure number.
|
|
11385
|
-
// - If a chord root is not A-G, then ignore it as if the chord wasn't there at all.
|
|
11386
|
-
// - If a chord modification isn't in our supported list, change it to a major triad.
|
|
11387
|
-
//
|
|
11388
|
-
// - If there is only one chord in a measure:
|
|
11389
|
-
// - If 2/4, play root chord
|
|
11390
|
-
// - If cut time, play root(1) chord(3)
|
|
11391
|
-
// - If 3/4, play root chord chord
|
|
11392
|
-
// - If 4/4 or common time, play root chord fifth chord
|
|
11393
|
-
// - If 6/8, play root(1) chord(3) fifth(4) chord(6)
|
|
11394
|
-
// - For any other meter, play the full chord on each beat. (TODO-PER: expand this as more support is added.)
|
|
11395
|
-
//
|
|
11396
|
-
// - If there is a chord specified that is not on a beat, move it earlier to the previous beat, unless there is already a chord on that beat.
|
|
11397
|
-
// - Otherwise, move it later, unless there is already a chord on that beat.
|
|
11398
|
-
// - Otherwise, ignore it. (TODO-PER: expand this as more support is added.)
|
|
11399
|
-
//
|
|
11400
|
-
// - If there is a chord on the second beat, play a chord for the first beat instead of a bass note.
|
|
11401
|
-
// - Likewise, if there is a chord on the fourth beat of 4/4, play a chord on the third beat instead of a bass note.
|
|
11402
|
-
//
|
|
11403
|
-
// If there is any note in the melody that has a rhythm head, then assume the melody controls the rhythm, so that is
|
|
11404
|
-
// the same as a break.
|
|
11405
|
-
var breakSynonyms = ['break', '(break)', 'no chord', 'n.c.', 'tacet'];
|
|
11406
|
-
function findChord(elem) {
|
|
11407
|
-
if (gChordTacet) return 'break';
|
|
11408
|
-
|
|
11409
|
-
// TODO-PER: Just using the first chord if there are more than one.
|
|
11410
|
-
if (chordTrackFinished || !elem.chord || elem.chord.length === 0) return null;
|
|
11411
|
-
|
|
11412
|
-
// Return the first annotation that is a regular chord: that is, it is in the default place or is a recognized "tacet" phrase.
|
|
11413
|
-
for (var i = 0; i < elem.chord.length; i++) {
|
|
11414
|
-
var ch = elem.chord[i];
|
|
11415
|
-
if (ch.position === 'default') return ch.name;
|
|
11416
|
-
if (breakSynonyms.indexOf(ch.name.toLowerCase()) >= 0) return 'break';
|
|
11417
|
-
}
|
|
11418
|
-
return null;
|
|
11419
|
-
}
|
|
11420
11577
|
function calcBeat(measureStart, beatLength, currTime) {
|
|
11421
11578
|
var distanceFromStart = currTime - measureStart;
|
|
11422
11579
|
return distanceFromStart / beatLength;
|
|
@@ -11432,7 +11589,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11432
11589
|
} else if (pickupLength > beat) {
|
|
11433
11590
|
volume = stressBeatUp;
|
|
11434
11591
|
} else {
|
|
11435
|
-
var barLength = meter.num / meter.den;
|
|
11592
|
+
//var barLength = meter.num / meter.den;
|
|
11436
11593
|
var barBeat = calcBeat(lastBarTime, getBeatFraction(meter), beat);
|
|
11437
11594
|
if (barBeat === 0) volume = stressBeat1;else if (parseInt(barBeat, 10) === barBeat) volume = stressBeatDown;else volume = stressBeatUp;
|
|
11438
11595
|
}
|
|
@@ -11444,34 +11601,6 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11444
11601
|
if (volume > 127) volume = 127;
|
|
11445
11602
|
return voiceOff ? 0 : volume;
|
|
11446
11603
|
}
|
|
11447
|
-
function processChord(elem) {
|
|
11448
|
-
var firstChord = false;
|
|
11449
|
-
var chord = findChord(elem);
|
|
11450
|
-
if (chord) {
|
|
11451
|
-
var c = interpretChord(chord);
|
|
11452
|
-
// If this isn't a recognized chord, just completely ignore it.
|
|
11453
|
-
if (c) {
|
|
11454
|
-
// If we ever have a chord in this voice, then we add the chord track.
|
|
11455
|
-
// However, if there are chords on more than one voice, then just use the first voice.
|
|
11456
|
-
if (chordTrack.length === 0) {
|
|
11457
|
-
firstChord = true;
|
|
11458
|
-
chordTrack.push({
|
|
11459
|
-
cmd: 'program',
|
|
11460
|
-
channel: chordChannel,
|
|
11461
|
-
instrument: chordInstrument
|
|
11462
|
-
});
|
|
11463
|
-
}
|
|
11464
|
-
lastChord = c;
|
|
11465
|
-
var barBeat = calcBeat(lastBarTime, getBeatFraction(meter), timeToRealTime(elem.time));
|
|
11466
|
-
currentChords.push({
|
|
11467
|
-
chord: lastChord,
|
|
11468
|
-
beat: barBeat,
|
|
11469
|
-
start: timeToRealTime(elem.time)
|
|
11470
|
-
});
|
|
11471
|
-
}
|
|
11472
|
-
}
|
|
11473
|
-
return firstChord;
|
|
11474
|
-
}
|
|
11475
11604
|
function findNoteModifications(elem, velocity) {
|
|
11476
11605
|
var ret = {};
|
|
11477
11606
|
if (elem.decoration) {
|
|
@@ -11656,9 +11785,10 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11656
11785
|
// If there are guitar chords, then they are put in a separate track, but they have the same format.
|
|
11657
11786
|
//
|
|
11658
11787
|
|
|
11659
|
-
var trackStartingIndex = currentTrack.length;
|
|
11788
|
+
//var trackStartingIndex = currentTrack.length;
|
|
11789
|
+
|
|
11660
11790
|
var velocity = processVolume(timeToRealTime(elem.time), voiceOff);
|
|
11661
|
-
|
|
11791
|
+
chordTrack.processChord(elem);
|
|
11662
11792
|
|
|
11663
11793
|
// if there are grace notes, then also play them.
|
|
11664
11794
|
// I'm not sure there is an exact rule for the length of the notes. My rule, unless I find
|
|
@@ -11710,15 +11840,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11710
11840
|
// TODO-PER: Can also make a different sound on style=x and style=harmonic
|
|
11711
11841
|
var ePitches = elem.pitches;
|
|
11712
11842
|
if (elem.style === "rhythm") {
|
|
11713
|
-
|
|
11714
|
-
if (lastChord && lastChord.chick) {
|
|
11715
|
-
ePitches = [];
|
|
11716
|
-
for (var i2 = 0; i2 < lastChord.chick.length; i2++) {
|
|
11717
|
-
var note2 = parseCommon.clone(elem.pitches[0]);
|
|
11718
|
-
note2.actualPitch = lastChord.chick[i2];
|
|
11719
|
-
ePitches.push(note2);
|
|
11720
|
-
}
|
|
11721
|
-
}
|
|
11843
|
+
ePitches = chordTrack.setRhythmHead(true, elem);
|
|
11722
11844
|
}
|
|
11723
11845
|
if (elem.elem) elem.elem.midiPitches = [];
|
|
11724
11846
|
for (var i = 0; i < ePitches.length; i++) {
|
|
@@ -11770,7 +11892,6 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11770
11892
|
}
|
|
11771
11893
|
var realDur = getRealDuration(elem);
|
|
11772
11894
|
lastEventTime = Math.max(lastEventTime, timeToRealTime(elem.time) + durationRounded(realDur));
|
|
11773
|
-
return setChordTrack;
|
|
11774
11895
|
}
|
|
11775
11896
|
function getRealDuration(elem) {
|
|
11776
11897
|
if (elem.pitches && elem.pitches.length > 0 && elem.pitches[0]) return elem.pitches[0].duration;
|
|
@@ -11919,349 +12040,6 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11919
12040
|
if (pitch < 0) pitch += 7;
|
|
11920
12041
|
return pitch;
|
|
11921
12042
|
}
|
|
11922
|
-
var basses = {
|
|
11923
|
-
'A': 33,
|
|
11924
|
-
'B': 35,
|
|
11925
|
-
'C': 36,
|
|
11926
|
-
'D': 38,
|
|
11927
|
-
'E': 40,
|
|
11928
|
-
'F': 41,
|
|
11929
|
-
'G': 43
|
|
11930
|
-
};
|
|
11931
|
-
function interpretChord(name) {
|
|
11932
|
-
// chords have the format:
|
|
11933
|
-
// [root][acc][modifier][/][bass][acc]
|
|
11934
|
-
// (The chord might be surrounded by parens. Just ignore them.)
|
|
11935
|
-
// root must be present and must be from A-G.
|
|
11936
|
-
// acc is optional and can be # or b
|
|
11937
|
-
// The modifier can be a wide variety of things, like "maj7". As they are discovered, more are supported here.
|
|
11938
|
-
// If there is a slash, then there is a bass note, which can be from A-G, with an optional acc.
|
|
11939
|
-
// If the root is unrecognized, then "undefined" is returned and there is no chord.
|
|
11940
|
-
// If the modifier is unrecognized, a major triad is returned.
|
|
11941
|
-
// If the bass notes is unrecognized, it is ignored.
|
|
11942
|
-
if (name.length === 0) return undefined;
|
|
11943
|
-
if (name === 'break') return {
|
|
11944
|
-
chick: []
|
|
11945
|
-
};
|
|
11946
|
-
var root = name.substring(0, 1);
|
|
11947
|
-
if (root === '(') {
|
|
11948
|
-
name = name.substring(1, name.length - 2);
|
|
11949
|
-
if (name.length === 0) return undefined;
|
|
11950
|
-
root = name.substring(0, 1);
|
|
11951
|
-
}
|
|
11952
|
-
var bass = basses[root];
|
|
11953
|
-
if (!bass)
|
|
11954
|
-
// If the bass note isn't listed, then this was an unknown root. Only A-G are accepted.
|
|
11955
|
-
return undefined;
|
|
11956
|
-
// Don't transpose the chords more than an octave.
|
|
11957
|
-
var chordTranspose = transpose;
|
|
11958
|
-
while (chordTranspose < -8) {
|
|
11959
|
-
chordTranspose += 12;
|
|
11960
|
-
}
|
|
11961
|
-
while (chordTranspose > 8) {
|
|
11962
|
-
chordTranspose -= 12;
|
|
11963
|
-
}
|
|
11964
|
-
bass += chordTranspose;
|
|
11965
|
-
var bass2 = bass - 5; // The alternating bass is a 4th below
|
|
11966
|
-
var chick;
|
|
11967
|
-
if (name.length === 1) chick = chordNotes(bass, '');
|
|
11968
|
-
var remaining = name.substring(1);
|
|
11969
|
-
var acc = remaining.substring(0, 1);
|
|
11970
|
-
if (acc === 'b' || acc === '♭') {
|
|
11971
|
-
bass--;
|
|
11972
|
-
bass2--;
|
|
11973
|
-
remaining = remaining.substring(1);
|
|
11974
|
-
} else if (acc === '#' || acc === '♯') {
|
|
11975
|
-
bass++;
|
|
11976
|
-
bass2++;
|
|
11977
|
-
remaining = remaining.substring(1);
|
|
11978
|
-
}
|
|
11979
|
-
var arr = remaining.split('/');
|
|
11980
|
-
chick = chordNotes(bass, arr[0]);
|
|
11981
|
-
// If the 5th is altered then the bass is altered. Normally the bass is 7 from the root, so adjust if it isn't.
|
|
11982
|
-
if (chick.length >= 3) {
|
|
11983
|
-
var fifth = chick[2] - chick[0];
|
|
11984
|
-
bass2 = bass2 + fifth - 7;
|
|
11985
|
-
}
|
|
11986
|
-
if (arr.length === 2) {
|
|
11987
|
-
var explicitBass = basses[arr[1].substring(0, 1)];
|
|
11988
|
-
if (explicitBass) {
|
|
11989
|
-
var bassAcc = arr[1].substring(1);
|
|
11990
|
-
var bassShift = {
|
|
11991
|
-
'#': 1,
|
|
11992
|
-
'♯': 1,
|
|
11993
|
-
'b': -1,
|
|
11994
|
-
'♭': -1
|
|
11995
|
-
}[bassAcc] || 0;
|
|
11996
|
-
bass = basses[arr[1].substring(0, 1)] + bassShift + chordTranspose;
|
|
11997
|
-
bass2 = bass;
|
|
11998
|
-
}
|
|
11999
|
-
}
|
|
12000
|
-
return {
|
|
12001
|
-
boom: bass,
|
|
12002
|
-
boom2: bass2,
|
|
12003
|
-
chick: chick
|
|
12004
|
-
};
|
|
12005
|
-
}
|
|
12006
|
-
var chordIntervals = {
|
|
12007
|
-
// diminished (all flat 5 chords)
|
|
12008
|
-
'dim': [0, 3, 6],
|
|
12009
|
-
'°': [0, 3, 6],
|
|
12010
|
-
'˚': [0, 3, 6],
|
|
12011
|
-
'dim7': [0, 3, 6, 9],
|
|
12012
|
-
'°7': [0, 3, 6, 9],
|
|
12013
|
-
'˚7': [0, 3, 6, 9],
|
|
12014
|
-
'ø7': [0, 3, 6, 10],
|
|
12015
|
-
'm7(b5)': [0, 3, 6, 10],
|
|
12016
|
-
'm7b5': [0, 3, 6, 10],
|
|
12017
|
-
'm7♭5': [0, 3, 6, 10],
|
|
12018
|
-
'-7(b5)': [0, 3, 6, 10],
|
|
12019
|
-
'-7b5': [0, 3, 6, 10],
|
|
12020
|
-
'7b5': [0, 4, 6, 10],
|
|
12021
|
-
'7(b5)': [0, 4, 6, 10],
|
|
12022
|
-
'7♭5': [0, 4, 6, 10],
|
|
12023
|
-
'7(b9,b5)': [0, 4, 6, 10, 13],
|
|
12024
|
-
'7b9,b5': [0, 4, 6, 10, 13],
|
|
12025
|
-
'7(#9,b5)': [0, 4, 6, 10, 15],
|
|
12026
|
-
'7#9b5': [0, 4, 6, 10, 15],
|
|
12027
|
-
'maj7(b5)': [0, 4, 6, 11],
|
|
12028
|
-
'maj7b5': [0, 4, 6, 11],
|
|
12029
|
-
'13(b5)': [0, 4, 6, 10, 14, 21],
|
|
12030
|
-
'13b5': [0, 4, 6, 10, 14, 21],
|
|
12031
|
-
// minor (all normal 5, minor 3 chords)
|
|
12032
|
-
'm': [0, 3, 7],
|
|
12033
|
-
'-': [0, 3, 7],
|
|
12034
|
-
'm6': [0, 3, 7, 9],
|
|
12035
|
-
'-6': [0, 3, 7, 9],
|
|
12036
|
-
'm7': [0, 3, 7, 10],
|
|
12037
|
-
'-7': [0, 3, 7, 10],
|
|
12038
|
-
'-(b6)': [0, 3, 7, 8],
|
|
12039
|
-
'-b6': [0, 3, 7, 8],
|
|
12040
|
-
'-6/9': [0, 3, 7, 9, 14],
|
|
12041
|
-
'-7(b9)': [0, 3, 7, 10, 13],
|
|
12042
|
-
'-7b9': [0, 3, 7, 10, 13],
|
|
12043
|
-
'-maj7': [0, 3, 7, 11],
|
|
12044
|
-
'-9+7': [0, 3, 7, 11, 13],
|
|
12045
|
-
'-11': [0, 3, 7, 11, 14, 17],
|
|
12046
|
-
'm11': [0, 3, 7, 11, 14, 17],
|
|
12047
|
-
'-maj9': [0, 3, 7, 11, 14],
|
|
12048
|
-
'-∆9': [0, 3, 7, 11, 14],
|
|
12049
|
-
'mM9': [0, 3, 7, 11, 14],
|
|
12050
|
-
// major (all normal 5, major 3 chords)
|
|
12051
|
-
'M': [0, 4, 7],
|
|
12052
|
-
'6': [0, 4, 7, 9],
|
|
12053
|
-
'6/9': [0, 4, 7, 9, 14],
|
|
12054
|
-
'6add9': [0, 4, 7, 9, 14],
|
|
12055
|
-
'69': [0, 4, 7, 9, 14],
|
|
12056
|
-
'7': [0, 4, 7, 10],
|
|
12057
|
-
'9': [0, 4, 7, 10, 14],
|
|
12058
|
-
'11': [0, 7, 10, 14, 17],
|
|
12059
|
-
'13': [0, 4, 7, 10, 14, 21],
|
|
12060
|
-
'7b9': [0, 4, 7, 10, 13],
|
|
12061
|
-
'7♭9': [0, 4, 7, 10, 13],
|
|
12062
|
-
'7(b9)': [0, 4, 7, 10, 13],
|
|
12063
|
-
'7(#9)': [0, 4, 7, 10, 15],
|
|
12064
|
-
'7#9': [0, 4, 7, 10, 15],
|
|
12065
|
-
'(13)': [0, 4, 7, 10, 14, 21],
|
|
12066
|
-
'7(9,13)': [0, 4, 7, 10, 14, 21],
|
|
12067
|
-
'7(#9,b13)': [0, 4, 7, 10, 15, 20],
|
|
12068
|
-
'7(#11)': [0, 4, 7, 10, 14, 18],
|
|
12069
|
-
'7#11': [0, 4, 7, 10, 14, 18],
|
|
12070
|
-
'7(b13)': [0, 4, 7, 10, 20],
|
|
12071
|
-
'7b13': [0, 4, 7, 10, 20],
|
|
12072
|
-
'9(#11)': [0, 4, 7, 10, 14, 18],
|
|
12073
|
-
'9#11': [0, 4, 7, 10, 14, 18],
|
|
12074
|
-
'13(#11)': [0, 4, 7, 10, 18, 21],
|
|
12075
|
-
'13#11': [0, 4, 7, 10, 18, 21],
|
|
12076
|
-
'maj7': [0, 4, 7, 11],
|
|
12077
|
-
'∆7': [0, 4, 7, 11],
|
|
12078
|
-
'Δ7': [0, 4, 7, 11],
|
|
12079
|
-
'maj9': [0, 4, 7, 11, 14],
|
|
12080
|
-
'maj7(9)': [0, 4, 7, 11, 14],
|
|
12081
|
-
'maj7(11)': [0, 4, 7, 11, 17],
|
|
12082
|
-
'maj7(#11)': [0, 4, 7, 11, 18],
|
|
12083
|
-
'maj7(13)': [0, 4, 7, 14, 21],
|
|
12084
|
-
'maj7(9,13)': [0, 4, 7, 11, 14, 21],
|
|
12085
|
-
'7sus4': [0, 5, 7, 10],
|
|
12086
|
-
'm7sus4': [0, 3, 7, 10, 17],
|
|
12087
|
-
'sus4': [0, 5, 7],
|
|
12088
|
-
'sus2': [0, 2, 7],
|
|
12089
|
-
'7sus2': [0, 2, 7, 10],
|
|
12090
|
-
'9sus4': [0, 5, 7, 10, 14],
|
|
12091
|
-
'13sus4': [0, 5, 7, 10, 14, 21],
|
|
12092
|
-
// augmented (all sharp 5 chords)
|
|
12093
|
-
'aug7': [0, 4, 8, 10],
|
|
12094
|
-
'+7': [0, 4, 8, 10],
|
|
12095
|
-
'+': [0, 4, 8],
|
|
12096
|
-
'7#5': [0, 4, 8, 10],
|
|
12097
|
-
'7♯5': [0, 4, 8, 10],
|
|
12098
|
-
'7+5': [0, 4, 8, 10],
|
|
12099
|
-
'9#5': [0, 4, 8, 10, 14],
|
|
12100
|
-
'9♯5': [0, 4, 8, 10, 14],
|
|
12101
|
-
'9+5': [0, 4, 8, 10, 14],
|
|
12102
|
-
'-7(#5)': [0, 3, 8, 10],
|
|
12103
|
-
'-7#5': [0, 3, 8, 10],
|
|
12104
|
-
'7(#5)': [0, 4, 8, 10],
|
|
12105
|
-
'7(b9,#5)': [0, 4, 8, 10, 13],
|
|
12106
|
-
'7b9#5': [0, 4, 8, 10, 13],
|
|
12107
|
-
'maj7(#5)': [0, 4, 8, 11],
|
|
12108
|
-
'maj7#5': [0, 4, 8, 11],
|
|
12109
|
-
'maj7(#5,#11)': [0, 4, 8, 11, 18],
|
|
12110
|
-
'maj7#5#11': [0, 4, 8, 11, 18],
|
|
12111
|
-
'9(#5)': [0, 4, 8, 10, 14],
|
|
12112
|
-
'13(#5)': [0, 4, 8, 10, 14, 21],
|
|
12113
|
-
'13#5': [0, 4, 8, 10, 14, 21]
|
|
12114
|
-
};
|
|
12115
|
-
function chordNotes(bass, modifier) {
|
|
12116
|
-
var intervals = chordIntervals[modifier];
|
|
12117
|
-
if (!intervals) {
|
|
12118
|
-
if (modifier.slice(0, 2).toLowerCase() === 'ma' || modifier[0] === 'M') intervals = chordIntervals.M;else if (modifier[0] === 'm' || modifier[0] === '-') intervals = chordIntervals.m;else intervals = chordIntervals.M;
|
|
12119
|
-
}
|
|
12120
|
-
bass += 12; // the chord is an octave above the bass note.
|
|
12121
|
-
var notes = [];
|
|
12122
|
-
for (var i = 0; i < intervals.length; i++) {
|
|
12123
|
-
notes.push(bass + intervals[i]);
|
|
12124
|
-
}
|
|
12125
|
-
return notes;
|
|
12126
|
-
}
|
|
12127
|
-
function writeBoom(boom, beatLength, volume, beat, noteLength) {
|
|
12128
|
-
// undefined means there is a stop time.
|
|
12129
|
-
if (boom !== undefined) chordTrack.push({
|
|
12130
|
-
cmd: 'note',
|
|
12131
|
-
pitch: boom,
|
|
12132
|
-
volume: volume,
|
|
12133
|
-
start: lastBarTime + beat * durationRounded(beatLength),
|
|
12134
|
-
duration: durationRounded(noteLength),
|
|
12135
|
-
gap: 0,
|
|
12136
|
-
instrument: bassInstrument
|
|
12137
|
-
});
|
|
12138
|
-
}
|
|
12139
|
-
function writeChick(chick, beatLength, volume, beat, noteLength) {
|
|
12140
|
-
for (var c = 0; c < chick.length; c++) {
|
|
12141
|
-
chordTrack.push({
|
|
12142
|
-
cmd: 'note',
|
|
12143
|
-
pitch: chick[c],
|
|
12144
|
-
volume: volume,
|
|
12145
|
-
start: lastBarTime + beat * durationRounded(beatLength),
|
|
12146
|
-
duration: durationRounded(noteLength),
|
|
12147
|
-
gap: 0,
|
|
12148
|
-
instrument: chordInstrument
|
|
12149
|
-
});
|
|
12150
|
-
}
|
|
12151
|
-
}
|
|
12152
|
-
var rhythmPatterns = {
|
|
12153
|
-
"2/2": ['boom', 'chick'],
|
|
12154
|
-
"2/4": ['boom', 'chick'],
|
|
12155
|
-
"3/4": ['boom', 'chick', 'chick'],
|
|
12156
|
-
"4/4": ['boom', 'chick', 'boom2', 'chick'],
|
|
12157
|
-
"5/4": ['boom', 'chick', 'chick', 'boom2', 'chick'],
|
|
12158
|
-
"6/8": ['boom', '', 'chick', 'boom2', '', 'chick'],
|
|
12159
|
-
"9/8": ['boom', '', 'chick', 'boom2', '', 'chick', 'boom2', '', 'chick'],
|
|
12160
|
-
"12/8": ['boom', '', 'chick', 'boom2', '', 'chick', 'boom', '', 'chick', 'boom2', '', 'chick']
|
|
12161
|
-
};
|
|
12162
|
-
function resolveChords(startTime, endTime) {
|
|
12163
|
-
var num = meter.num;
|
|
12164
|
-
var den = meter.den;
|
|
12165
|
-
var beatLength = 1 / den;
|
|
12166
|
-
var noteLength = beatLength / 2;
|
|
12167
|
-
var pattern = rhythmPatterns[num + '/' + den];
|
|
12168
|
-
var thisMeasureLength = parseInt(num, 10) / parseInt(den, 10);
|
|
12169
|
-
var portionOfAMeasure = thisMeasureLength - (endTime - startTime) / tempoChangeFactor;
|
|
12170
|
-
if (Math.abs(portionOfAMeasure) < 0.00001) portionOfAMeasure = false;
|
|
12171
|
-
if (!pattern || portionOfAMeasure) {
|
|
12172
|
-
// If it is an unsupported meter, or this isn't a full bar, just chick on each beat.
|
|
12173
|
-
pattern = [];
|
|
12174
|
-
var beatsPresent = (endTime - startTime) / tempoChangeFactor / beatLength;
|
|
12175
|
-
for (var p = 0; p < beatsPresent; p++) {
|
|
12176
|
-
pattern.push("chick");
|
|
12177
|
-
}
|
|
12178
|
-
}
|
|
12179
|
-
//console.log(startTime, pattern, currentChords, lastChord, portionOfAMeasure)
|
|
12180
|
-
|
|
12181
|
-
if (currentChords.length === 0) {
|
|
12182
|
-
// there wasn't a new chord this measure, so use the last chord declared.
|
|
12183
|
-
currentChords.push({
|
|
12184
|
-
beat: 0,
|
|
12185
|
-
chord: lastChord
|
|
12186
|
-
});
|
|
12187
|
-
}
|
|
12188
|
-
if (currentChords[0].beat !== 0 && lastChord) {
|
|
12189
|
-
// this is the case where there is a chord declared in the measure, but not on its first beat.
|
|
12190
|
-
if (chordLastBar) currentChords.unshift({
|
|
12191
|
-
beat: 0,
|
|
12192
|
-
chord: chordLastBar
|
|
12193
|
-
});
|
|
12194
|
-
}
|
|
12195
|
-
if (currentChords.length === 1) {
|
|
12196
|
-
for (var m = currentChords[0].beat; m < pattern.length; m++) {
|
|
12197
|
-
if (!hasRhythmHead) {
|
|
12198
|
-
switch (pattern[m]) {
|
|
12199
|
-
case 'boom':
|
|
12200
|
-
writeBoom(currentChords[0].chord.boom, beatLength, boomVolume, m, noteLength);
|
|
12201
|
-
break;
|
|
12202
|
-
case 'boom2':
|
|
12203
|
-
writeBoom(currentChords[0].chord.boom2, beatLength, boomVolume, m, noteLength);
|
|
12204
|
-
break;
|
|
12205
|
-
case 'chick':
|
|
12206
|
-
writeChick(currentChords[0].chord.chick, beatLength, chickVolume, m, noteLength);
|
|
12207
|
-
break;
|
|
12208
|
-
}
|
|
12209
|
-
}
|
|
12210
|
-
}
|
|
12211
|
-
return;
|
|
12212
|
-
}
|
|
12213
|
-
|
|
12214
|
-
// If we are here it is because more than one chord was declared in the measure, so we have to sort out what chord goes where.
|
|
12215
|
-
|
|
12216
|
-
// First, normalize the chords on beats.
|
|
12217
|
-
var mult = beatLength === 0.125 ? 3 : 1; // If this is a compound meter then the beats in the currentChords is 1/3 of the true beat
|
|
12218
|
-
var beats = {};
|
|
12219
|
-
for (var i = 0; i < currentChords.length; i++) {
|
|
12220
|
-
var cc = currentChords[i];
|
|
12221
|
-
var b = Math.round(cc.beat * mult);
|
|
12222
|
-
beats['' + b] = cc;
|
|
12223
|
-
}
|
|
12224
|
-
|
|
12225
|
-
// - If there is a chord on the second beat, play a chord for the first beat instead of a bass note.
|
|
12226
|
-
// - Likewise, if there is a chord on the fourth beat of 4/4, play a chord on the third beat instead of a bass note.
|
|
12227
|
-
for (var m2 = 0; m2 < pattern.length; m2++) {
|
|
12228
|
-
var thisChord;
|
|
12229
|
-
if (beats['' + m2]) thisChord = beats['' + m2];
|
|
12230
|
-
var lastBoom;
|
|
12231
|
-
if (!hasRhythmHead && thisChord) {
|
|
12232
|
-
switch (pattern[m2]) {
|
|
12233
|
-
case 'boom':
|
|
12234
|
-
if (beats['' + (m2 + 1)])
|
|
12235
|
-
// If there is not a chord change on the next beat, play a bass note.
|
|
12236
|
-
writeChick(thisChord.chord.chick, beatLength, chickVolume, m2, noteLength);else {
|
|
12237
|
-
writeBoom(thisChord.chord.boom, beatLength, boomVolume, m2, noteLength);
|
|
12238
|
-
lastBoom = thisChord.chord.boom;
|
|
12239
|
-
}
|
|
12240
|
-
break;
|
|
12241
|
-
case 'boom2':
|
|
12242
|
-
if (beats['' + (m2 + 1)]) writeChick(thisChord.chord.chick, beatLength, chickVolume, m2, noteLength);else {
|
|
12243
|
-
// If there is the same root as the last chord, use the alternating bass, otherwise play the root.
|
|
12244
|
-
if (lastBoom === thisChord.chord.boom) {
|
|
12245
|
-
writeBoom(thisChord.chord.boom2, beatLength, boomVolume, m2, noteLength);
|
|
12246
|
-
lastBoom = undefined;
|
|
12247
|
-
} else {
|
|
12248
|
-
writeBoom(thisChord.chord.boom, beatLength, boomVolume, m2, noteLength);
|
|
12249
|
-
lastBoom = thisChord.chord.boom;
|
|
12250
|
-
}
|
|
12251
|
-
}
|
|
12252
|
-
break;
|
|
12253
|
-
case 'chick':
|
|
12254
|
-
writeChick(thisChord.chord.chick, beatLength, chickVolume, m2, noteLength);
|
|
12255
|
-
break;
|
|
12256
|
-
case '':
|
|
12257
|
-
if (beats['' + m2])
|
|
12258
|
-
// If there is an explicit chord on this beat, play it.
|
|
12259
|
-
writeChick(thisChord.chord.chick, beatLength, chickVolume, m2, noteLength);
|
|
12260
|
-
break;
|
|
12261
|
-
}
|
|
12262
|
-
}
|
|
12263
|
-
}
|
|
12264
|
-
}
|
|
12265
12043
|
function normalizeDrumDefinition(params) {
|
|
12266
12044
|
// Be very strict with the drum definition. If anything is not perfect,
|
|
12267
12045
|
// just turn the drums off.
|
|
@@ -12754,6 +12532,7 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
12754
12532
|
var drumBars = options.drumBars || 1;
|
|
12755
12533
|
var drumIntro = options.drumIntro || 0;
|
|
12756
12534
|
var drumOn = drumPattern !== "";
|
|
12535
|
+
var drumOffAfterIntro = !!options.drumOff;
|
|
12757
12536
|
var style = []; // The note head style for each voice.
|
|
12758
12537
|
var rhythmHeadThisBar = false; // Rhythm notation was detected.
|
|
12759
12538
|
var crescendoSize = 50; // how much to increase or decrease volume when crescendo/diminuendo is encountered.
|
|
@@ -13207,13 +12986,13 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
13207
12986
|
break;
|
|
13208
12987
|
case "gchordoff":
|
|
13209
12988
|
voices[voiceNumber].push({
|
|
13210
|
-
el_type: '
|
|
12989
|
+
el_type: 'gchordOn',
|
|
13211
12990
|
tacet: true
|
|
13212
12991
|
});
|
|
13213
12992
|
break;
|
|
13214
12993
|
case "gchordon":
|
|
13215
12994
|
voices[voiceNumber].push({
|
|
13216
|
-
el_type: '
|
|
12995
|
+
el_type: 'gchordOn',
|
|
13217
12996
|
tacet: false
|
|
13218
12997
|
});
|
|
13219
12998
|
break;
|
|
@@ -13236,15 +13015,21 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
13236
13015
|
});
|
|
13237
13016
|
break;
|
|
13238
13017
|
case "vol":
|
|
13018
|
+
case "volinc":
|
|
13239
13019
|
voices[voiceNumber].push({
|
|
13240
|
-
el_type:
|
|
13020
|
+
el_type: elem.cmd,
|
|
13241
13021
|
volume: elem.params[0]
|
|
13242
13022
|
});
|
|
13243
13023
|
break;
|
|
13244
|
-
case "
|
|
13024
|
+
case "swing":
|
|
13025
|
+
case "gchord":
|
|
13026
|
+
case "bassprog":
|
|
13027
|
+
case "chordprog":
|
|
13028
|
+
case "bassvol":
|
|
13029
|
+
case "chordvol":
|
|
13245
13030
|
voices[voiceNumber].push({
|
|
13246
|
-
el_type:
|
|
13247
|
-
|
|
13031
|
+
el_type: elem.cmd,
|
|
13032
|
+
param: elem.params[0]
|
|
13248
13033
|
});
|
|
13249
13034
|
break;
|
|
13250
13035
|
default:
|
|
@@ -13286,16 +13071,19 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
13286
13071
|
if (voices[vv].length > insertPoint) {
|
|
13287
13072
|
for (var w = 0; w < drumIntro; w++) {
|
|
13288
13073
|
// If it is the last measure of intro, subtract the pickups.
|
|
13289
|
-
if (pickups === 0 || w < drumIntro - 1)
|
|
13290
|
-
el_type: "note",
|
|
13291
|
-
rest: {
|
|
13292
|
-
type: "rest"
|
|
13293
|
-
},
|
|
13294
|
-
duration: measureLength
|
|
13295
|
-
}, {
|
|
13296
|
-
el_type: "bar"
|
|
13297
|
-
});else {
|
|
13074
|
+
if (pickups === 0 || w < drumIntro - 1) {
|
|
13298
13075
|
voices[vv].splice(insertPoint, 0, {
|
|
13076
|
+
el_type: "note",
|
|
13077
|
+
rest: {
|
|
13078
|
+
type: "rest"
|
|
13079
|
+
},
|
|
13080
|
+
duration: measureLength
|
|
13081
|
+
}, {
|
|
13082
|
+
el_type: "bar"
|
|
13083
|
+
});
|
|
13084
|
+
insertPoint += 2;
|
|
13085
|
+
} else {
|
|
13086
|
+
voices[vv].splice(insertPoint++, 0, {
|
|
13299
13087
|
el_type: "note",
|
|
13300
13088
|
rest: {
|
|
13301
13089
|
type: "rest"
|
|
@@ -13304,6 +13092,19 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
13304
13092
|
});
|
|
13305
13093
|
}
|
|
13306
13094
|
}
|
|
13095
|
+
if (drumOffAfterIntro) {
|
|
13096
|
+
drumOn = false;
|
|
13097
|
+
voices[vv].splice(insertPoint++, 0, {
|
|
13098
|
+
el_type: 'drum',
|
|
13099
|
+
params: {
|
|
13100
|
+
pattern: drumPattern,
|
|
13101
|
+
bars: drumBars,
|
|
13102
|
+
intro: drumIntro,
|
|
13103
|
+
on: drumOn
|
|
13104
|
+
}
|
|
13105
|
+
});
|
|
13106
|
+
drumOffAfterIntro = false;
|
|
13107
|
+
}
|
|
13307
13108
|
}
|
|
13308
13109
|
}
|
|
13309
13110
|
}
|
|
@@ -13499,6 +13300,589 @@ module.exports = centsToFactor;
|
|
|
13499
13300
|
|
|
13500
13301
|
/***/ }),
|
|
13501
13302
|
|
|
13303
|
+
/***/ "./src/synth/chord-track.js":
|
|
13304
|
+
/*!**********************************!*\
|
|
13305
|
+
!*** ./src/synth/chord-track.js ***!
|
|
13306
|
+
\**********************************/
|
|
13307
|
+
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
13308
|
+
|
|
13309
|
+
//
|
|
13310
|
+
// The algorithm for chords is:
|
|
13311
|
+
// - The chords are done in a separate track.
|
|
13312
|
+
// - If there are notes before the first chord, then put that much silence to start the track.
|
|
13313
|
+
// - The pattern of chord expression depends on the meter, and how many chords are in a measure.
|
|
13314
|
+
// - There is a possibility that a measure will have an incorrect number of beats, if that is the case, then
|
|
13315
|
+
// start the pattern anew on the next measure number.
|
|
13316
|
+
// - If a chord root is not A-G, then ignore it as if the chord wasn't there at all.
|
|
13317
|
+
// - If a chord modification isn't in our supported list, change it to a major triad.
|
|
13318
|
+
//
|
|
13319
|
+
// - There is a standard pattern of boom-chick for each time sig, or it can be overridden.
|
|
13320
|
+
// - For any unrecognized meter, play the full chord on each beat.
|
|
13321
|
+
//
|
|
13322
|
+
// - If there is a chord specified that is not on a beat, move it earlier to the previous beat, unless there is already a chord on that beat.
|
|
13323
|
+
// - Otherwise, move it later, unless there is already a chord on that beat.
|
|
13324
|
+
// - Otherwise, ignore it. (TODO-PER: expand this as more support is added.)
|
|
13325
|
+
//
|
|
13326
|
+
// If there is any note in the melody that has a rhythm head, then assume the melody controls the rhythm, so there is no chord added for that entire measure.
|
|
13327
|
+
|
|
13328
|
+
var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/abc_common.js");
|
|
13329
|
+
var ChordTrack = function ChordTrack(numVoices, chordsOff, midiOptions, meter) {
|
|
13330
|
+
this.chordTrack = [];
|
|
13331
|
+
this.chordTrackFinished = false;
|
|
13332
|
+
this.chordChannel = numVoices; // first free channel for chords
|
|
13333
|
+
this.currentChords = [];
|
|
13334
|
+
this.lastChord;
|
|
13335
|
+
this.chordLastBar;
|
|
13336
|
+
this.chordsOff = !!chordsOff;
|
|
13337
|
+
this.gChordTacet = this.chordsOff;
|
|
13338
|
+
this.hasRhythmHead = false;
|
|
13339
|
+
this.transpose = 0;
|
|
13340
|
+
this.lastBarTime = 0;
|
|
13341
|
+
this.meter = meter;
|
|
13342
|
+
this.tempoChangeFactor = 1;
|
|
13343
|
+
this.bassInstrument = midiOptions.bassprog && midiOptions.bassprog.length === 1 ? midiOptions.bassprog[0] : 0;
|
|
13344
|
+
this.chordInstrument = midiOptions.chordprog && midiOptions.chordprog.length === 1 ? midiOptions.chordprog[0] : 0;
|
|
13345
|
+
this.boomVolume = midiOptions.bassvol && midiOptions.bassvol.length === 1 ? midiOptions.bassvol[0] : 64;
|
|
13346
|
+
this.chickVolume = midiOptions.chordvol && midiOptions.chordvol.length === 1 ? midiOptions.chordvol[0] : 48;
|
|
13347
|
+
this.overridePattern = midiOptions.gchord ? parseGChord(midiOptions.gchord[0]) : undefined;
|
|
13348
|
+
};
|
|
13349
|
+
ChordTrack.prototype.setMeter = function (meter) {
|
|
13350
|
+
this.meter = meter;
|
|
13351
|
+
};
|
|
13352
|
+
ChordTrack.prototype.setTempoChangeFactor = function (tempoChangeFactor) {
|
|
13353
|
+
this.tempoChangeFactor = tempoChangeFactor;
|
|
13354
|
+
};
|
|
13355
|
+
ChordTrack.prototype.setLastBarTime = function (lastBarTime) {
|
|
13356
|
+
this.lastBarTime = lastBarTime;
|
|
13357
|
+
};
|
|
13358
|
+
ChordTrack.prototype.setTranspose = function (transpose) {
|
|
13359
|
+
this.transpose = transpose;
|
|
13360
|
+
};
|
|
13361
|
+
ChordTrack.prototype.setRhythmHead = function (isRhythmHead, elem) {
|
|
13362
|
+
this.hasRhythmHead = isRhythmHead;
|
|
13363
|
+
var ePitches = [];
|
|
13364
|
+
if (isRhythmHead) {
|
|
13365
|
+
if (this.lastChord && this.lastChord.chick) {
|
|
13366
|
+
for (var i2 = 0; i2 < this.lastChord.chick.length; i2++) {
|
|
13367
|
+
var note2 = parseCommon.clone(elem.pitches[0]);
|
|
13368
|
+
note2.actualPitch = this.lastChord.chick[i2];
|
|
13369
|
+
ePitches.push(note2);
|
|
13370
|
+
}
|
|
13371
|
+
}
|
|
13372
|
+
}
|
|
13373
|
+
return ePitches;
|
|
13374
|
+
};
|
|
13375
|
+
ChordTrack.prototype.barEnd = function (element) {
|
|
13376
|
+
if (this.chordTrack.length > 0 && !this.chordTrackFinished) {
|
|
13377
|
+
this.resolveChords(this.lastBarTime, timeToRealTime(element.time));
|
|
13378
|
+
this.currentChords = [];
|
|
13379
|
+
}
|
|
13380
|
+
this.chordLastBar = this.lastChord;
|
|
13381
|
+
};
|
|
13382
|
+
ChordTrack.prototype.gChordOn = function (element) {
|
|
13383
|
+
if (!this.chordsOff) this.gChordTacet = element.tacet;
|
|
13384
|
+
};
|
|
13385
|
+
ChordTrack.prototype.paramChange = function (element) {
|
|
13386
|
+
switch (element.el_type) {
|
|
13387
|
+
case "gchord":
|
|
13388
|
+
this.overridePattern = parseGChord(element.param);
|
|
13389
|
+
break;
|
|
13390
|
+
case "bassprog":
|
|
13391
|
+
this.bassInstrument = element.param;
|
|
13392
|
+
break;
|
|
13393
|
+
case "chordprog":
|
|
13394
|
+
this.chordInstrument = element.param;
|
|
13395
|
+
break;
|
|
13396
|
+
case "bassvol":
|
|
13397
|
+
this.boomVolume = element.param;
|
|
13398
|
+
break;
|
|
13399
|
+
case "chordvol":
|
|
13400
|
+
this.chickVolume = element.param;
|
|
13401
|
+
break;
|
|
13402
|
+
default:
|
|
13403
|
+
console.log("unhandled midi param", element);
|
|
13404
|
+
}
|
|
13405
|
+
};
|
|
13406
|
+
ChordTrack.prototype.finish = function () {
|
|
13407
|
+
if (!this.chordTrackEmpty())
|
|
13408
|
+
// Don't do chords on more than one track, so turn off chord detection after we create it.
|
|
13409
|
+
this.chordTrackFinished = true;
|
|
13410
|
+
};
|
|
13411
|
+
ChordTrack.prototype.addTrack = function (tracks) {
|
|
13412
|
+
if (!this.chordTrackEmpty()) tracks.push(this.chordTrack);
|
|
13413
|
+
};
|
|
13414
|
+
ChordTrack.prototype.findChord = function (elem) {
|
|
13415
|
+
if (this.gChordTacet) return 'break';
|
|
13416
|
+
|
|
13417
|
+
// TODO-PER: Just using the first chord if there are more than one.
|
|
13418
|
+
if (this.chordTrackFinished || !elem.chord || elem.chord.length === 0) return null;
|
|
13419
|
+
|
|
13420
|
+
// Return the first annotation that is a regular chord: that is, it is in the default place or is a recognized "tacet" phrase.
|
|
13421
|
+
for (var i = 0; i < elem.chord.length; i++) {
|
|
13422
|
+
var ch = elem.chord[i];
|
|
13423
|
+
if (ch.position === 'default') return ch.name;
|
|
13424
|
+
if (this.breakSynonyms.indexOf(ch.name.toLowerCase()) >= 0) return 'break';
|
|
13425
|
+
}
|
|
13426
|
+
return null;
|
|
13427
|
+
};
|
|
13428
|
+
ChordTrack.prototype.interpretChord = function (name) {
|
|
13429
|
+
// chords have the format:
|
|
13430
|
+
// [root][acc][modifier][/][bass][acc]
|
|
13431
|
+
// (The chord might be surrounded by parens. Just ignore them.)
|
|
13432
|
+
// root must be present and must be from A-G.
|
|
13433
|
+
// acc is optional and can be # or b
|
|
13434
|
+
// The modifier can be a wide variety of things, like "maj7". As they are discovered, more are supported here.
|
|
13435
|
+
// If there is a slash, then there is a bass note, which can be from A-G, with an optional acc.
|
|
13436
|
+
// If the root is unrecognized, then "undefined" is returned and there is no chord.
|
|
13437
|
+
// If the modifier is unrecognized, a major triad is returned.
|
|
13438
|
+
// If the bass notes is unrecognized, it is ignored.
|
|
13439
|
+
if (name.length === 0) return undefined;
|
|
13440
|
+
if (name === 'break') return {
|
|
13441
|
+
chick: []
|
|
13442
|
+
};
|
|
13443
|
+
var root = name.substring(0, 1);
|
|
13444
|
+
if (root === '(') {
|
|
13445
|
+
name = name.substring(1, name.length - 2);
|
|
13446
|
+
if (name.length === 0) return undefined;
|
|
13447
|
+
root = name.substring(0, 1);
|
|
13448
|
+
}
|
|
13449
|
+
var bass = this.basses[root];
|
|
13450
|
+
if (!bass)
|
|
13451
|
+
// If the bass note isn't listed, then this was an unknown root. Only A-G are accepted.
|
|
13452
|
+
return undefined;
|
|
13453
|
+
// Don't transpose the chords more than an octave.
|
|
13454
|
+
var chordTranspose = this.transpose;
|
|
13455
|
+
while (chordTranspose < -8) {
|
|
13456
|
+
chordTranspose += 12;
|
|
13457
|
+
}
|
|
13458
|
+
while (chordTranspose > 8) {
|
|
13459
|
+
chordTranspose -= 12;
|
|
13460
|
+
}
|
|
13461
|
+
bass += chordTranspose;
|
|
13462
|
+
var bass2 = bass - 5; // The alternating bass is a 4th below
|
|
13463
|
+
var chick;
|
|
13464
|
+
if (name.length === 1) chick = this.chordNotes(bass, '');
|
|
13465
|
+
var remaining = name.substring(1);
|
|
13466
|
+
var acc = remaining.substring(0, 1);
|
|
13467
|
+
if (acc === 'b' || acc === '♭') {
|
|
13468
|
+
bass--;
|
|
13469
|
+
bass2--;
|
|
13470
|
+
remaining = remaining.substring(1);
|
|
13471
|
+
} else if (acc === '#' || acc === '♯') {
|
|
13472
|
+
bass++;
|
|
13473
|
+
bass2++;
|
|
13474
|
+
remaining = remaining.substring(1);
|
|
13475
|
+
}
|
|
13476
|
+
var arr = remaining.split('/');
|
|
13477
|
+
chick = this.chordNotes(bass, arr[0]);
|
|
13478
|
+
// If the 5th is altered then the bass is altered. Normally the bass is 7 from the root, so adjust if it isn't.
|
|
13479
|
+
if (chick.length >= 3) {
|
|
13480
|
+
var fifth = chick[2] - chick[0];
|
|
13481
|
+
bass2 = bass2 + fifth - 7;
|
|
13482
|
+
}
|
|
13483
|
+
if (arr.length === 2) {
|
|
13484
|
+
var explicitBass = this.basses[arr[1].substring(0, 1)];
|
|
13485
|
+
if (explicitBass) {
|
|
13486
|
+
var bassAcc = arr[1].substring(1);
|
|
13487
|
+
var bassShift = {
|
|
13488
|
+
'#': 1,
|
|
13489
|
+
'♯': 1,
|
|
13490
|
+
'b': -1,
|
|
13491
|
+
'♭': -1
|
|
13492
|
+
}[bassAcc] || 0;
|
|
13493
|
+
bass = this.basses[arr[1].substring(0, 1)] + bassShift + chordTranspose;
|
|
13494
|
+
bass2 = bass;
|
|
13495
|
+
}
|
|
13496
|
+
}
|
|
13497
|
+
return {
|
|
13498
|
+
boom: bass,
|
|
13499
|
+
boom2: bass2,
|
|
13500
|
+
chick: chick
|
|
13501
|
+
};
|
|
13502
|
+
};
|
|
13503
|
+
ChordTrack.prototype.chordNotes = function (bass, modifier) {
|
|
13504
|
+
var intervals = this.chordIntervals[modifier];
|
|
13505
|
+
if (!intervals) {
|
|
13506
|
+
if (modifier.slice(0, 2).toLowerCase() === 'ma' || modifier[0] === 'M') intervals = this.chordIntervals.M;else if (modifier[0] === 'm' || modifier[0] === '-') intervals = this.chordIntervals.m;else intervals = this.chordIntervals.M;
|
|
13507
|
+
}
|
|
13508
|
+
bass += 12; // the chord is an octave above the bass note.
|
|
13509
|
+
var notes = [];
|
|
13510
|
+
for (var i = 0; i < intervals.length; i++) {
|
|
13511
|
+
notes.push(bass + intervals[i]);
|
|
13512
|
+
}
|
|
13513
|
+
return notes;
|
|
13514
|
+
};
|
|
13515
|
+
ChordTrack.prototype.writeNote = function (note, beatLength, volume, beat, noteLength, instrument) {
|
|
13516
|
+
// undefined means there is a stop time.
|
|
13517
|
+
if (note !== undefined) this.chordTrack.push({
|
|
13518
|
+
cmd: 'note',
|
|
13519
|
+
pitch: note,
|
|
13520
|
+
volume: volume,
|
|
13521
|
+
start: this.lastBarTime + beat * durationRounded(beatLength, this.tempoChangeFactor),
|
|
13522
|
+
duration: durationRounded(noteLength, this.tempoChangeFactor),
|
|
13523
|
+
gap: 0,
|
|
13524
|
+
instrument: instrument
|
|
13525
|
+
});
|
|
13526
|
+
};
|
|
13527
|
+
ChordTrack.prototype.chordTrackEmpty = function () {
|
|
13528
|
+
var isEmpty = true;
|
|
13529
|
+
for (var i = 0; i < this.chordTrack.length && isEmpty; i++) {
|
|
13530
|
+
if (this.chordTrack[i].cmd === 'note') isEmpty = false;
|
|
13531
|
+
}
|
|
13532
|
+
return isEmpty;
|
|
13533
|
+
};
|
|
13534
|
+
ChordTrack.prototype.resolveChords = function (startTime, endTime) {
|
|
13535
|
+
// If there is a rhythm head anywhere in the measure then don't add a separate rhythm track
|
|
13536
|
+
if (this.hasRhythmHead) return;
|
|
13537
|
+
var num = this.meter.num;
|
|
13538
|
+
var den = this.meter.den;
|
|
13539
|
+
var beatLength = 1 / den;
|
|
13540
|
+
var noteLength = beatLength / 2;
|
|
13541
|
+
var thisMeasureLength = parseInt(num, 10) / parseInt(den, 10);
|
|
13542
|
+
var portionOfAMeasure = thisMeasureLength - (endTime - startTime) / this.tempoChangeFactor;
|
|
13543
|
+
if (Math.abs(portionOfAMeasure) < 0.00001) portionOfAMeasure = 0;
|
|
13544
|
+
|
|
13545
|
+
// there wasn't a new chord this measure, so use the last chord declared.
|
|
13546
|
+
// also the case where there is a chord declared in the measure, but not on its first beat.
|
|
13547
|
+
if (this.currentChords.length === 0 || this.currentChords[0].beat !== 0) {
|
|
13548
|
+
this.currentChords.unshift({
|
|
13549
|
+
beat: 0,
|
|
13550
|
+
chord: this.chordLastBar
|
|
13551
|
+
});
|
|
13552
|
+
}
|
|
13553
|
+
|
|
13554
|
+
//console.log(this.currentChords)
|
|
13555
|
+
var currentChordsExpanded = expandCurrentChords(this.currentChords, 8 * num / den, beatLength);
|
|
13556
|
+
//console.log(currentChordsExpanded)
|
|
13557
|
+
var thisPattern = this.overridePattern ? this.overridePattern : this.rhythmPatterns[num + '/' + den];
|
|
13558
|
+
if (portionOfAMeasure) {
|
|
13559
|
+
thisPattern = [];
|
|
13560
|
+
var beatsPresent = (endTime - startTime) / this.tempoChangeFactor * 8;
|
|
13561
|
+
for (var p = 0; p < beatsPresent / 2; p += 2) {
|
|
13562
|
+
thisPattern.push("chick");
|
|
13563
|
+
thisPattern.push("");
|
|
13564
|
+
}
|
|
13565
|
+
}
|
|
13566
|
+
if (!thisPattern) {
|
|
13567
|
+
thisPattern = [];
|
|
13568
|
+
for (var p = 0; p < 8 * num / den / 2; p++) {
|
|
13569
|
+
thisPattern.push('chick');
|
|
13570
|
+
thisPattern.push("");
|
|
13571
|
+
}
|
|
13572
|
+
}
|
|
13573
|
+
var firstBoom = true;
|
|
13574
|
+
// If the pattern is overridden, it might be longer than the length of a measure. If so, then ignore the rest of it
|
|
13575
|
+
var minLength = Math.min(thisPattern.length, currentChordsExpanded.length);
|
|
13576
|
+
for (var p = 0; p < minLength; p++) {
|
|
13577
|
+
if (p > 0 && currentChordsExpanded[p - 1] && currentChordsExpanded[p] && currentChordsExpanded[p - 1].boom !== currentChordsExpanded[p].boom) firstBoom = true;
|
|
13578
|
+
var type = thisPattern[p];
|
|
13579
|
+
var isBoom = type.indexOf('boom') >= 0;
|
|
13580
|
+
// If we changed chords at a time when we're not expecting a bass note, then add an extra bass note in.
|
|
13581
|
+
var newBass = !isBoom && p !== 0 && (!currentChordsExpanded[p - 1] || currentChordsExpanded[p - 1].boom !== currentChordsExpanded[p].boom);
|
|
13582
|
+
var pitches = resolvePitch(currentChordsExpanded[p], type, firstBoom, newBass);
|
|
13583
|
+
if (isBoom) firstBoom = false;
|
|
13584
|
+
for (var oo = 0; oo < pitches.length; oo++) {
|
|
13585
|
+
this.writeNote(pitches[oo], 0.125, isBoom || newBass ? this.boomVolume : this.chickVolume, p, noteLength, isBoom || newBass ? this.bassInstrument : this.chordInstrument);
|
|
13586
|
+
if (newBass) newBass = false;else isBoom = false; // only the first note in a chord is a bass note. This handles the case where bass and chord are played at the same time.
|
|
13587
|
+
}
|
|
13588
|
+
}
|
|
13589
|
+
|
|
13590
|
+
return;
|
|
13591
|
+
};
|
|
13592
|
+
ChordTrack.prototype.processChord = function (elem) {
|
|
13593
|
+
if (this.chordTrackFinished) return;
|
|
13594
|
+
var chord = this.findChord(elem);
|
|
13595
|
+
if (chord) {
|
|
13596
|
+
var c = this.interpretChord(chord);
|
|
13597
|
+
// If this isn't a recognized chord, just completely ignore it.
|
|
13598
|
+
if (c) {
|
|
13599
|
+
// If we ever have a chord in this voice, then we add the chord track.
|
|
13600
|
+
// However, if there are chords on more than one voice, then just use the first voice.
|
|
13601
|
+
if (this.chordTrack.length === 0) {
|
|
13602
|
+
this.chordTrack.push({
|
|
13603
|
+
cmd: 'program',
|
|
13604
|
+
channel: this.chordChannel,
|
|
13605
|
+
instrument: this.chordInstrument
|
|
13606
|
+
});
|
|
13607
|
+
}
|
|
13608
|
+
this.lastChord = c;
|
|
13609
|
+
var barBeat = calcBeat(this.lastBarTime, timeToRealTime(elem.time));
|
|
13610
|
+
this.currentChords.push({
|
|
13611
|
+
chord: this.lastChord,
|
|
13612
|
+
beat: barBeat,
|
|
13613
|
+
start: timeToRealTime(elem.time)
|
|
13614
|
+
});
|
|
13615
|
+
}
|
|
13616
|
+
}
|
|
13617
|
+
};
|
|
13618
|
+
function resolvePitch(currentChord, type, firstBoom, newBass) {
|
|
13619
|
+
var ret = [];
|
|
13620
|
+
if (!currentChord) return ret;
|
|
13621
|
+
if (type.indexOf('boom') >= 0) ret.push(firstBoom ? currentChord.boom : currentChord.boom2);else if (newBass) ret.push(currentChord.boom);
|
|
13622
|
+
if (type.indexOf('chick') >= 0) {
|
|
13623
|
+
for (var i = 0; i < currentChord.chick.length; i++) {
|
|
13624
|
+
ret.push(currentChord.chick[i]);
|
|
13625
|
+
}
|
|
13626
|
+
}
|
|
13627
|
+
switch (type) {
|
|
13628
|
+
case 'DO':
|
|
13629
|
+
ret.push(currentChord.chick[0]);
|
|
13630
|
+
break;
|
|
13631
|
+
case 'MI':
|
|
13632
|
+
ret.push(currentChord.chick[1]);
|
|
13633
|
+
break;
|
|
13634
|
+
case 'SOL':
|
|
13635
|
+
ret.push(currentChord.chick[2]);
|
|
13636
|
+
break;
|
|
13637
|
+
case 'TI':
|
|
13638
|
+
currentChord.chick.length > 3 ? ret.push(currentChord.chick[2]) : ret.push(currentChord.chick[0] + 12);
|
|
13639
|
+
break;
|
|
13640
|
+
case 'TOP':
|
|
13641
|
+
currentChord.chick.length > 4 ? ret.push(currentChord.chick[2]) : ret.push(currentChord.chick[1] + 12);
|
|
13642
|
+
break;
|
|
13643
|
+
case 'do':
|
|
13644
|
+
ret.push(currentChord.chick[0] + 12);
|
|
13645
|
+
break;
|
|
13646
|
+
case 'mi':
|
|
13647
|
+
ret.push(currentChord.chick[1] + 12);
|
|
13648
|
+
break;
|
|
13649
|
+
case 'sol':
|
|
13650
|
+
ret.push(currentChord.chick[2] + 12);
|
|
13651
|
+
break;
|
|
13652
|
+
case 'ti':
|
|
13653
|
+
currentChord.chick.length > 3 ? ret.push(currentChord.chick[2] + 12) : ret.push(currentChord.chick[0] + 24);
|
|
13654
|
+
break;
|
|
13655
|
+
case 'top':
|
|
13656
|
+
currentChord.chick.length > 4 ? ret.push(currentChord.chick[2] + 12) : ret.push(currentChord.chick[1] + 24);
|
|
13657
|
+
break;
|
|
13658
|
+
}
|
|
13659
|
+
return ret;
|
|
13660
|
+
}
|
|
13661
|
+
function parseGChord(gchord) {
|
|
13662
|
+
// TODO-PER: The spec is more complicated than this but for now this will not try to do anything with error cases like the wrong number of beats.
|
|
13663
|
+
var pattern = [];
|
|
13664
|
+
for (var i = 0; i < gchord.length; i++) {
|
|
13665
|
+
var ch = gchord[i];
|
|
13666
|
+
switch (ch) {
|
|
13667
|
+
case 'z':
|
|
13668
|
+
pattern.push('');
|
|
13669
|
+
break;
|
|
13670
|
+
case '2':
|
|
13671
|
+
pattern.push('');
|
|
13672
|
+
break;
|
|
13673
|
+
// TODO-PER: This should extend the last note, but that's a small effect
|
|
13674
|
+
case 'c':
|
|
13675
|
+
pattern.push('chick');
|
|
13676
|
+
break;
|
|
13677
|
+
case 'b':
|
|
13678
|
+
pattern.push('boom&chick');
|
|
13679
|
+
break;
|
|
13680
|
+
case 'f':
|
|
13681
|
+
pattern.push('boom');
|
|
13682
|
+
break;
|
|
13683
|
+
case 'G':
|
|
13684
|
+
pattern.push('DO');
|
|
13685
|
+
break;
|
|
13686
|
+
case 'H':
|
|
13687
|
+
pattern.push('MI');
|
|
13688
|
+
break;
|
|
13689
|
+
case 'I':
|
|
13690
|
+
pattern.push('SOL');
|
|
13691
|
+
break;
|
|
13692
|
+
case 'J':
|
|
13693
|
+
pattern.push('TI');
|
|
13694
|
+
break;
|
|
13695
|
+
case 'K':
|
|
13696
|
+
pattern.push('TOP');
|
|
13697
|
+
break;
|
|
13698
|
+
case 'g':
|
|
13699
|
+
pattern.push('do');
|
|
13700
|
+
break;
|
|
13701
|
+
case 'h':
|
|
13702
|
+
pattern.push('mi');
|
|
13703
|
+
break;
|
|
13704
|
+
case 'i':
|
|
13705
|
+
pattern.push('sol');
|
|
13706
|
+
break;
|
|
13707
|
+
case 'j':
|
|
13708
|
+
pattern.push('ti');
|
|
13709
|
+
break;
|
|
13710
|
+
case 'k':
|
|
13711
|
+
pattern.push('top');
|
|
13712
|
+
break;
|
|
13713
|
+
}
|
|
13714
|
+
}
|
|
13715
|
+
return pattern;
|
|
13716
|
+
}
|
|
13717
|
+
|
|
13718
|
+
// This returns an array that has a chord for each 1/8th note position in the current measure
|
|
13719
|
+
function expandCurrentChords(currentChords, num8thNotes, beatLength) {
|
|
13720
|
+
beatLength = beatLength * 8; // this is expressed as a fraction, so that 0.25 is a quarter notes. We want it to be the number of 8th notes
|
|
13721
|
+
var chords = [];
|
|
13722
|
+
if (currentChords.length === 0) return chords;
|
|
13723
|
+
var currentChord = currentChords[0].chord;
|
|
13724
|
+
for (var i = 1; i < currentChords.length; i++) {
|
|
13725
|
+
var current = currentChords[i];
|
|
13726
|
+
while (chords.length < current.beat) {
|
|
13727
|
+
chords.push(currentChord);
|
|
13728
|
+
}
|
|
13729
|
+
currentChord = current.chord;
|
|
13730
|
+
}
|
|
13731
|
+
while (chords.length < num8thNotes) {
|
|
13732
|
+
chords.push(currentChord);
|
|
13733
|
+
}
|
|
13734
|
+
return chords;
|
|
13735
|
+
}
|
|
13736
|
+
function calcBeat(measureStart, currTime) {
|
|
13737
|
+
var distanceFromStart = currTime - measureStart;
|
|
13738
|
+
return distanceFromStart * 8;
|
|
13739
|
+
}
|
|
13740
|
+
ChordTrack.prototype.breakSynonyms = ['break', '(break)', 'no chord', 'n.c.', 'tacet'];
|
|
13741
|
+
ChordTrack.prototype.basses = {
|
|
13742
|
+
'A': 33,
|
|
13743
|
+
'B': 35,
|
|
13744
|
+
'C': 36,
|
|
13745
|
+
'D': 38,
|
|
13746
|
+
'E': 40,
|
|
13747
|
+
'F': 41,
|
|
13748
|
+
'G': 43
|
|
13749
|
+
};
|
|
13750
|
+
ChordTrack.prototype.chordIntervals = {
|
|
13751
|
+
// diminished (all flat 5 chords)
|
|
13752
|
+
'dim': [0, 3, 6],
|
|
13753
|
+
'°': [0, 3, 6],
|
|
13754
|
+
'˚': [0, 3, 6],
|
|
13755
|
+
'dim7': [0, 3, 6, 9],
|
|
13756
|
+
'°7': [0, 3, 6, 9],
|
|
13757
|
+
'˚7': [0, 3, 6, 9],
|
|
13758
|
+
'ø7': [0, 3, 6, 10],
|
|
13759
|
+
'm7(b5)': [0, 3, 6, 10],
|
|
13760
|
+
'm7b5': [0, 3, 6, 10],
|
|
13761
|
+
'm7♭5': [0, 3, 6, 10],
|
|
13762
|
+
'-7(b5)': [0, 3, 6, 10],
|
|
13763
|
+
'-7b5': [0, 3, 6, 10],
|
|
13764
|
+
'7b5': [0, 4, 6, 10],
|
|
13765
|
+
'7(b5)': [0, 4, 6, 10],
|
|
13766
|
+
'7♭5': [0, 4, 6, 10],
|
|
13767
|
+
'7(b9,b5)': [0, 4, 6, 10, 13],
|
|
13768
|
+
'7b9,b5': [0, 4, 6, 10, 13],
|
|
13769
|
+
'7(#9,b5)': [0, 4, 6, 10, 15],
|
|
13770
|
+
'7#9b5': [0, 4, 6, 10, 15],
|
|
13771
|
+
'maj7(b5)': [0, 4, 6, 11],
|
|
13772
|
+
'maj7b5': [0, 4, 6, 11],
|
|
13773
|
+
'13(b5)': [0, 4, 6, 10, 14, 21],
|
|
13774
|
+
'13b5': [0, 4, 6, 10, 14, 21],
|
|
13775
|
+
// minor (all normal 5, minor 3 chords)
|
|
13776
|
+
'm': [0, 3, 7],
|
|
13777
|
+
'-': [0, 3, 7],
|
|
13778
|
+
'm6': [0, 3, 7, 9],
|
|
13779
|
+
'-6': [0, 3, 7, 9],
|
|
13780
|
+
'm7': [0, 3, 7, 10],
|
|
13781
|
+
'-7': [0, 3, 7, 10],
|
|
13782
|
+
'-(b6)': [0, 3, 7, 8],
|
|
13783
|
+
'-b6': [0, 3, 7, 8],
|
|
13784
|
+
'-6/9': [0, 3, 7, 9, 14],
|
|
13785
|
+
'-7(b9)': [0, 3, 7, 10, 13],
|
|
13786
|
+
'-7b9': [0, 3, 7, 10, 13],
|
|
13787
|
+
'-maj7': [0, 3, 7, 11],
|
|
13788
|
+
'-9+7': [0, 3, 7, 11, 13],
|
|
13789
|
+
'-11': [0, 3, 7, 11, 14, 17],
|
|
13790
|
+
'm11': [0, 3, 7, 11, 14, 17],
|
|
13791
|
+
'-maj9': [0, 3, 7, 11, 14],
|
|
13792
|
+
'-∆9': [0, 3, 7, 11, 14],
|
|
13793
|
+
'mM9': [0, 3, 7, 11, 14],
|
|
13794
|
+
// major (all normal 5, major 3 chords)
|
|
13795
|
+
'M': [0, 4, 7],
|
|
13796
|
+
'6': [0, 4, 7, 9],
|
|
13797
|
+
'6/9': [0, 4, 7, 9, 14],
|
|
13798
|
+
'6add9': [0, 4, 7, 9, 14],
|
|
13799
|
+
'69': [0, 4, 7, 9, 14],
|
|
13800
|
+
'7': [0, 4, 7, 10],
|
|
13801
|
+
'9': [0, 4, 7, 10, 14],
|
|
13802
|
+
'11': [0, 7, 10, 14, 17],
|
|
13803
|
+
'13': [0, 4, 7, 10, 14, 21],
|
|
13804
|
+
'7b9': [0, 4, 7, 10, 13],
|
|
13805
|
+
'7♭9': [0, 4, 7, 10, 13],
|
|
13806
|
+
'7(b9)': [0, 4, 7, 10, 13],
|
|
13807
|
+
'7(#9)': [0, 4, 7, 10, 15],
|
|
13808
|
+
'7#9': [0, 4, 7, 10, 15],
|
|
13809
|
+
'(13)': [0, 4, 7, 10, 14, 21],
|
|
13810
|
+
'7(9,13)': [0, 4, 7, 10, 14, 21],
|
|
13811
|
+
'7(#9,b13)': [0, 4, 7, 10, 15, 20],
|
|
13812
|
+
'7(#11)': [0, 4, 7, 10, 14, 18],
|
|
13813
|
+
'7#11': [0, 4, 7, 10, 14, 18],
|
|
13814
|
+
'7(b13)': [0, 4, 7, 10, 20],
|
|
13815
|
+
'7b13': [0, 4, 7, 10, 20],
|
|
13816
|
+
'9(#11)': [0, 4, 7, 10, 14, 18],
|
|
13817
|
+
'9#11': [0, 4, 7, 10, 14, 18],
|
|
13818
|
+
'13(#11)': [0, 4, 7, 10, 18, 21],
|
|
13819
|
+
'13#11': [0, 4, 7, 10, 18, 21],
|
|
13820
|
+
'maj7': [0, 4, 7, 11],
|
|
13821
|
+
'∆7': [0, 4, 7, 11],
|
|
13822
|
+
'Δ7': [0, 4, 7, 11],
|
|
13823
|
+
'maj9': [0, 4, 7, 11, 14],
|
|
13824
|
+
'maj7(9)': [0, 4, 7, 11, 14],
|
|
13825
|
+
'maj7(11)': [0, 4, 7, 11, 17],
|
|
13826
|
+
'maj7(#11)': [0, 4, 7, 11, 18],
|
|
13827
|
+
'maj7(13)': [0, 4, 7, 14, 21],
|
|
13828
|
+
'maj7(9,13)': [0, 4, 7, 11, 14, 21],
|
|
13829
|
+
'7sus4': [0, 5, 7, 10],
|
|
13830
|
+
'm7sus4': [0, 3, 7, 10, 17],
|
|
13831
|
+
'sus4': [0, 5, 7],
|
|
13832
|
+
'sus2': [0, 2, 7],
|
|
13833
|
+
'7sus2': [0, 2, 7, 10],
|
|
13834
|
+
'9sus4': [0, 5, 7, 10, 14],
|
|
13835
|
+
'13sus4': [0, 5, 7, 10, 14, 21],
|
|
13836
|
+
// augmented (all sharp 5 chords)
|
|
13837
|
+
'aug7': [0, 4, 8, 10],
|
|
13838
|
+
'+7': [0, 4, 8, 10],
|
|
13839
|
+
'+': [0, 4, 8],
|
|
13840
|
+
'7#5': [0, 4, 8, 10],
|
|
13841
|
+
'7♯5': [0, 4, 8, 10],
|
|
13842
|
+
'7+5': [0, 4, 8, 10],
|
|
13843
|
+
'9#5': [0, 4, 8, 10, 14],
|
|
13844
|
+
'9♯5': [0, 4, 8, 10, 14],
|
|
13845
|
+
'9+5': [0, 4, 8, 10, 14],
|
|
13846
|
+
'-7(#5)': [0, 3, 8, 10],
|
|
13847
|
+
'-7#5': [0, 3, 8, 10],
|
|
13848
|
+
'7(#5)': [0, 4, 8, 10],
|
|
13849
|
+
'7(b9,#5)': [0, 4, 8, 10, 13],
|
|
13850
|
+
'7b9#5': [0, 4, 8, 10, 13],
|
|
13851
|
+
'maj7(#5)': [0, 4, 8, 11],
|
|
13852
|
+
'maj7#5': [0, 4, 8, 11],
|
|
13853
|
+
'maj7(#5,#11)': [0, 4, 8, 11, 18],
|
|
13854
|
+
'maj7#5#11': [0, 4, 8, 11, 18],
|
|
13855
|
+
'9(#5)': [0, 4, 8, 10, 14],
|
|
13856
|
+
'13(#5)': [0, 4, 8, 10, 14, 21],
|
|
13857
|
+
'13#5': [0, 4, 8, 10, 14, 21]
|
|
13858
|
+
};
|
|
13859
|
+
ChordTrack.prototype.rhythmPatterns = {
|
|
13860
|
+
"2/2": ['boom', '', '', '', 'chick', '', '', ''],
|
|
13861
|
+
"3/2": ['boom', '', '', '', 'chick', '', '', '', 'chick', '', '', ''],
|
|
13862
|
+
"4/2": ['boom', '', '', '', 'chick', '', '', '', 'boom', '', '', '', 'chick', '', '', ''],
|
|
13863
|
+
"2/4": ['boom', '', 'chick', ''],
|
|
13864
|
+
"3/4": ['boom', '', 'chick', '', 'chick', ''],
|
|
13865
|
+
"4/4": ['boom', '', 'chick', '', 'boom', '', 'chick', ''],
|
|
13866
|
+
"5/4": ['boom', '', 'chick', '', 'chick', '', 'boom', '', 'chick', ''],
|
|
13867
|
+
"6/4": ['boom', '', 'chick', '', 'boom', '', 'chick', '', 'boom', '', 'chick', ''],
|
|
13868
|
+
"3/8": ['boom', '', 'chick'],
|
|
13869
|
+
"6/8": ['boom', '', 'chick', 'boom', '', 'chick'],
|
|
13870
|
+
"9/8": ['boom', '', 'chick', 'boom', '', 'chick', 'boom', '', 'chick'],
|
|
13871
|
+
"12/8": ['boom', '', 'chick', 'boom', '', 'chick', 'boom', '', 'chick', 'boom', '', 'chick']
|
|
13872
|
+
};
|
|
13873
|
+
|
|
13874
|
+
// TODO-PER: these are repeated in flattener. Can it be shared?
|
|
13875
|
+
|
|
13876
|
+
function timeToRealTime(time) {
|
|
13877
|
+
return time / 1000000;
|
|
13878
|
+
}
|
|
13879
|
+
function durationRounded(duration, tempoChangeFactor) {
|
|
13880
|
+
return Math.round(duration * tempoChangeFactor * 1000000) / 1000000;
|
|
13881
|
+
}
|
|
13882
|
+
module.exports = ChordTrack;
|
|
13883
|
+
|
|
13884
|
+
/***/ }),
|
|
13885
|
+
|
|
13502
13886
|
/***/ "./src/synth/create-note-map.js":
|
|
13503
13887
|
/*!**************************************!*\
|
|
13504
13888
|
!*** ./src/synth/create-note-map.js ***!
|
|
@@ -13525,13 +13909,14 @@ var createNoteMap = function createNoteMap(sequence) {
|
|
|
13525
13909
|
// ev contains:
|
|
13526
13910
|
// {"cmd":"note","pitch":72,"volume":95,"start":0.125,"duration":0.25,"instrument":0,"gap":0}
|
|
13527
13911
|
// where start and duration are in whole notes, gap is in 1/1920 of a second (i.e. MIDI ticks)
|
|
13912
|
+
var inst = ev.instrument !== undefined ? instrumentIndexToName[ev.instrument] : currentInstrument;
|
|
13528
13913
|
if (ev.duration > 0) {
|
|
13529
13914
|
var gap = ev.gap ? ev.gap : 0;
|
|
13530
13915
|
var len = ev.duration;
|
|
13531
13916
|
gap = Math.min(gap, len * 2 / 3);
|
|
13532
13917
|
var obj = {
|
|
13533
13918
|
pitch: ev.pitch,
|
|
13534
|
-
instrument:
|
|
13919
|
+
instrument: inst,
|
|
13535
13920
|
start: Math.round(ev.start * 1000000) / 1000000,
|
|
13536
13921
|
end: Math.round((ev.start + len - gap) * 1000000) / 1000000,
|
|
13537
13922
|
volume: ev.volume
|
|
@@ -13794,12 +14179,13 @@ function CreateSynth() {
|
|
|
13794
14179
|
self.audioBuffers = []; // cache of the buffers so starting play can be fast.
|
|
13795
14180
|
self.duration = undefined; // the duration of the tune in seconds.
|
|
13796
14181
|
self.isRunning = false; // whether there is currently a sound buffer running.
|
|
13797
|
-
|
|
14182
|
+
self.options = undefined;
|
|
14183
|
+
self.pickupLength = 0;
|
|
13798
14184
|
|
|
13799
14185
|
// Load and cache all needed sounds
|
|
13800
14186
|
self.init = function (options) {
|
|
13801
14187
|
if (!options) options = {};
|
|
13802
|
-
|
|
14188
|
+
if (options.options) self.options = options.options;
|
|
13803
14189
|
registerAudioContext(options.audioContext); // This works no matter what - if there is already an ac it is a nop; if the context is not passed in, then it creates one.
|
|
13804
14190
|
var startTime = activeAudioContext().currentTime;
|
|
13805
14191
|
self.debugCallback = options.debugCallback;
|
|
@@ -13868,12 +14254,14 @@ function CreateSynth() {
|
|
|
13868
14254
|
self.flattened = options.visualObj.setUpAudio(params);
|
|
13869
14255
|
var meter = options.visualObj.getMeterFraction();
|
|
13870
14256
|
if (meter.den) self.meterSize = options.visualObj.getMeterFraction().num / options.visualObj.getMeterFraction().den;
|
|
14257
|
+
self.pickupLength = options.visualObj.getPickupLength();
|
|
13871
14258
|
} else if (options.sequence) self.flattened = options.sequence;else return Promise.reject(new Error("Must pass in either a visualObj or a sequence"));
|
|
13872
14259
|
self.millisecondsPerMeasure = options.millisecondsPerMeasure ? options.millisecondsPerMeasure : options.visualObj ? options.visualObj.millisecondsPerMeasure(self.flattened.tempo) : 1000;
|
|
13873
14260
|
self.beatsPerMeasure = options.visualObj ? options.visualObj.getBeatsPerMeasure() : 4;
|
|
13874
14261
|
self.sequenceCallback = params.sequenceCallback;
|
|
13875
14262
|
self.callbackContext = params.callbackContext;
|
|
13876
14263
|
self.onEnded = params.onEnded;
|
|
14264
|
+
self.meterFraction = options.visualObj.getMeterFraction();
|
|
13877
14265
|
var allNotes = {};
|
|
13878
14266
|
var cached = [];
|
|
13879
14267
|
var errorNotes = [];
|
|
@@ -13884,14 +14272,15 @@ function CreateSynth() {
|
|
|
13884
14272
|
if (event.pitch !== undefined) {
|
|
13885
14273
|
var pitchNumber = event.pitch;
|
|
13886
14274
|
var noteName = pitchToNoteName[pitchNumber];
|
|
14275
|
+
var inst = event.instrument !== undefined ? instrumentIndexToName[event.instrument] : currentInstrument;
|
|
13887
14276
|
if (noteName) {
|
|
13888
|
-
if (!allNotes[
|
|
13889
|
-
if (!soundsCache[
|
|
13890
|
-
var label2 =
|
|
14277
|
+
if (!allNotes[inst]) allNotes[inst] = {};
|
|
14278
|
+
if (!soundsCache[inst] || !soundsCache[inst][noteName]) allNotes[inst][noteName] = true;else {
|
|
14279
|
+
var label2 = inst + ":" + noteName;
|
|
13891
14280
|
if (cached.indexOf(label2) < 0) cached.push(label2);
|
|
13892
14281
|
}
|
|
13893
14282
|
} else {
|
|
13894
|
-
var label =
|
|
14283
|
+
var label = inst + ":" + noteName;
|
|
13895
14284
|
console.log("Can't find note: ", pitchNumber, label);
|
|
13896
14285
|
if (errorNotes.indexOf(label) < 0) errorNotes.push(label);
|
|
13897
14286
|
}
|
|
@@ -14032,8 +14421,7 @@ function CreateSynth() {
|
|
|
14032
14421
|
// There might be a previous run that needs to be turned off.
|
|
14033
14422
|
self.stop();
|
|
14034
14423
|
var noteMapTracks = createNoteMap(self.flattened);
|
|
14035
|
-
|
|
14036
|
-
// addSwing(noteMapTracks, self.options.swing, self.beatsPerMeasure)
|
|
14424
|
+
if (self.options.swing) addSwing(noteMapTracks, self.options.swing, self.meterFraction, self.pickupLength);
|
|
14037
14425
|
if (self.sequenceCallback) self.sequenceCallback(noteMapTracks, self.callbackContext);
|
|
14038
14426
|
var panDistances = setPan(noteMapTracks.length, self.pan);
|
|
14039
14427
|
|
|
@@ -14246,41 +14634,65 @@ function CreateSynth() {
|
|
|
14246
14634
|
};
|
|
14247
14635
|
}
|
|
14248
14636
|
};
|
|
14249
|
-
|
|
14250
|
-
|
|
14251
|
-
|
|
14252
|
-
|
|
14253
|
-
|
|
14254
|
-
|
|
14255
|
-
|
|
14256
|
-
|
|
14257
|
-
|
|
14258
|
-
|
|
14259
|
-
|
|
14260
|
-
|
|
14261
|
-
|
|
14262
|
-
|
|
14263
|
-
|
|
14264
|
-
|
|
14265
|
-
|
|
14266
|
-
|
|
14267
|
-
|
|
14268
|
-
|
|
14269
|
-
|
|
14270
|
-
|
|
14271
|
-
|
|
14272
|
-
|
|
14273
|
-
|
|
14274
|
-
|
|
14275
|
-
|
|
14276
|
-
|
|
14277
|
-
|
|
14278
|
-
|
|
14279
|
-
|
|
14280
|
-
|
|
14281
|
-
|
|
14637
|
+
function addSwing(noteMapTracks, swing, meterFraction, pickupLength) {
|
|
14638
|
+
// we can only swing in X/4 and X/8 meters.
|
|
14639
|
+
if (meterFraction.den != 4 && meterFraction.den != 8) return;
|
|
14640
|
+
swing = parseFloat(swing);
|
|
14641
|
+
|
|
14642
|
+
// 50 (or less) is no swing,
|
|
14643
|
+
if (isNaN(swing) || swing <= 50) return;
|
|
14644
|
+
|
|
14645
|
+
// 66 is triplet swing 2:1, and
|
|
14646
|
+
// 60 is swing with a ratio of 3:2.
|
|
14647
|
+
// 75 is the maximum swing where the first eight is played as a dotted eight and the second as a sixteenth.
|
|
14648
|
+
if (swing > 75) swing = 75;
|
|
14649
|
+
|
|
14650
|
+
// convert the swing percentage to a percentage of increase for the first half of the beat
|
|
14651
|
+
swing = swing / 50 - 1;
|
|
14652
|
+
|
|
14653
|
+
// The volume of the swung notes is increased by this factor
|
|
14654
|
+
// could be also in the settings. Try out values such 0.1, 0.2
|
|
14655
|
+
var volumeIncrease = 0.0;
|
|
14656
|
+
|
|
14657
|
+
// the beatLength in X/8 meters
|
|
14658
|
+
var beatLength = 0.25;
|
|
14659
|
+
|
|
14660
|
+
// in X/8 meters the 16s swing so the beatLength is halved
|
|
14661
|
+
if (meterFraction.den === 8) beatLength = beatLength / 2;
|
|
14662
|
+
|
|
14663
|
+
// duration of a half beat
|
|
14664
|
+
var halfbeatLength = beatLength / 2;
|
|
14665
|
+
|
|
14666
|
+
// the extra duration of the first swung notes and the delay of the second notes
|
|
14667
|
+
var swingDuration = halfbeatLength * swing;
|
|
14668
|
+
for (var t = 0; t < noteMapTracks.length; t++) {
|
|
14669
|
+
var track = noteMapTracks[t];
|
|
14670
|
+
for (var i = 0; i < track.length; i++) {
|
|
14671
|
+
var event = track[i];
|
|
14672
|
+
if (
|
|
14673
|
+
// is halfbeat
|
|
14674
|
+
(event.start - pickupLength) % halfbeatLength == 0 && (event.start - pickupLength) % beatLength != 0 && (
|
|
14675
|
+
// the previous note is on the beat or before OR there is no previous note
|
|
14676
|
+
i == 0 || track[i - 1].start <= track[i].start - halfbeatLength) && (
|
|
14677
|
+
// the next note is on the beat or after OR there is no next note
|
|
14678
|
+
i == track.length - 1 || track[i + 1].start >= track[i].start + halfbeatLength)) {
|
|
14679
|
+
var oldEventStart = event.start;
|
|
14680
|
+
event.start += swingDuration;
|
|
14681
|
+
|
|
14682
|
+
// Increase volume of swung notes
|
|
14683
|
+
event.volume *= 1 + volumeIncrease;
|
|
14684
|
+
|
|
14685
|
+
// if there is a previous note ending at the start of this note, extend its end
|
|
14686
|
+
// and decrease its volume
|
|
14687
|
+
if (i > 0 && track[i - 1].end == oldEventStart) {
|
|
14688
|
+
track[i - 1].end = event.start;
|
|
14689
|
+
track[i - 1].volume *= 1 - volumeIncrease;
|
|
14690
|
+
}
|
|
14691
|
+
}
|
|
14692
|
+
}
|
|
14693
|
+
}
|
|
14694
|
+
}
|
|
14282
14695
|
}
|
|
14283
|
-
|
|
14284
14696
|
module.exports = CreateSynth;
|
|
14285
14697
|
|
|
14286
14698
|
/***/ }),
|
|
@@ -15046,6 +15458,8 @@ function SynthController() {
|
|
|
15046
15458
|
self.isLoading = false;
|
|
15047
15459
|
self.load = function (selector, cursorControl, visualOptions) {
|
|
15048
15460
|
if (!visualOptions) visualOptions = {};
|
|
15461
|
+
if (visualOptions.displayPlay === undefined) visualOptions.displayPlay = true;
|
|
15462
|
+
if (visualOptions.displayProgress === undefined) visualOptions.displayProgress = true;
|
|
15049
15463
|
self.control = new CreateSynthControl(selector, {
|
|
15050
15464
|
loopHandler: visualOptions.displayLoop ? self.toggleLoop : undefined,
|
|
15051
15465
|
restartHandler: visualOptions.displayRestart ? self.restart : undefined,
|
|
@@ -15063,7 +15477,7 @@ function SynthController() {
|
|
|
15063
15477
|
self.setTune = function (visualObj, userAction, audioParams) {
|
|
15064
15478
|
self.visualObj = visualObj;
|
|
15065
15479
|
self.disable(false);
|
|
15066
|
-
self.options = audioParams;
|
|
15480
|
+
self.options = audioParams ? audioParams : {};
|
|
15067
15481
|
if (self.control) {
|
|
15068
15482
|
self.pause();
|
|
15069
15483
|
self.setProgress(0, 1);
|
|
@@ -15195,7 +15609,7 @@ function SynthController() {
|
|
|
15195
15609
|
};
|
|
15196
15610
|
self._randomAccess = function (ev) {
|
|
15197
15611
|
var background = ev.target.classList.contains('abcjs-midi-progress-indicator') ? ev.target.parentNode : ev.target;
|
|
15198
|
-
var percent = (ev.x - background.
|
|
15612
|
+
var percent = (ev.x - background.getBoundingClientRect().left) / background.offsetWidth;
|
|
15199
15613
|
if (percent < 0) percent = 0;
|
|
15200
15614
|
if (percent > 1) percent = 1;
|
|
15201
15615
|
self.seek(percent);
|
|
@@ -15337,87 +15751,6 @@ module.exports = SynthSequence;
|
|
|
15337
15751
|
|
|
15338
15752
|
/***/ }),
|
|
15339
15753
|
|
|
15340
|
-
/***/ "./src/tablatures/instruments/guitar/guitar-patterns.js":
|
|
15341
|
-
/*!**************************************************************!*\
|
|
15342
|
-
!*** ./src/tablatures/instruments/guitar/guitar-patterns.js ***!
|
|
15343
|
-
\**************************************************************/
|
|
15344
|
-
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
15345
|
-
|
|
15346
|
-
var StringPatterns = __webpack_require__(/*! ../string-patterns */ "./src/tablatures/instruments/string-patterns.js");
|
|
15347
|
-
function GuitarPatterns(plugin) {
|
|
15348
|
-
this.tuning = plugin._super.params.tuning;
|
|
15349
|
-
if (!this.tuning) {
|
|
15350
|
-
this.tuning = ['E,', 'A,', 'D', 'G', 'B', 'e'];
|
|
15351
|
-
}
|
|
15352
|
-
plugin.tuning = this.tuning;
|
|
15353
|
-
this.strings = new StringPatterns(plugin);
|
|
15354
|
-
}
|
|
15355
|
-
GuitarPatterns.prototype.notesToNumber = function (notes, graces) {
|
|
15356
|
-
var converter = this.strings;
|
|
15357
|
-
return converter.notesToNumber(notes, graces);
|
|
15358
|
-
};
|
|
15359
|
-
GuitarPatterns.prototype.stringToPitch = function (stringNumber) {
|
|
15360
|
-
var converter = this.strings;
|
|
15361
|
-
return converter.stringToPitch(stringNumber);
|
|
15362
|
-
};
|
|
15363
|
-
module.exports = GuitarPatterns;
|
|
15364
|
-
|
|
15365
|
-
/***/ }),
|
|
15366
|
-
|
|
15367
|
-
/***/ "./src/tablatures/instruments/guitar/tab-guitar.js":
|
|
15368
|
-
/*!*********************************************************!*\
|
|
15369
|
-
!*** ./src/tablatures/instruments/guitar/tab-guitar.js ***!
|
|
15370
|
-
\*********************************************************/
|
|
15371
|
-
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
15372
|
-
|
|
15373
|
-
/*
|
|
15374
|
-
Emit tab for Guitar staff
|
|
15375
|
-
*/
|
|
15376
|
-
var StringTablature = __webpack_require__(/*! ../string-tablature */ "./src/tablatures/instruments/string-tablature.js");
|
|
15377
|
-
var TabCommon = __webpack_require__(/*! ../../tab-common */ "./src/tablatures/tab-common.js");
|
|
15378
|
-
var TabRenderer = __webpack_require__(/*! ../../tab-renderer */ "./src/tablatures/tab-renderer.js");
|
|
15379
|
-
var GuitarPatterns = __webpack_require__(/*! ./guitar-patterns */ "./src/tablatures/instruments/guitar/guitar-patterns.js");
|
|
15380
|
-
|
|
15381
|
-
/**
|
|
15382
|
-
* upon init mainly store provided instances for later usage
|
|
15383
|
-
* @param {*} abcTune the parsed tune AST tree
|
|
15384
|
-
* @param {*} tuneNumber the parsed tune AST tree
|
|
15385
|
-
* @param {*} params complementary args provided to Tablature Plugin
|
|
15386
|
-
*/
|
|
15387
|
-
Plugin.prototype.init = function (abcTune, tuneNumber, params) {
|
|
15388
|
-
var _super = new TabCommon(abcTune, tuneNumber, params);
|
|
15389
|
-
this._super = _super;
|
|
15390
|
-
this.abcTune = abcTune;
|
|
15391
|
-
this.linePitch = 3;
|
|
15392
|
-
this.nbLines = 6;
|
|
15393
|
-
this.isTabBig = true;
|
|
15394
|
-
this.capo = params.capo;
|
|
15395
|
-
this.transpose = params.visualTranspose;
|
|
15396
|
-
this.tablature = new StringTablature(this.nbLines, this.linePitch);
|
|
15397
|
-
var semantics = new GuitarPatterns(this);
|
|
15398
|
-
this.semantics = semantics;
|
|
15399
|
-
};
|
|
15400
|
-
Plugin.prototype.render = function (renderer, line, staffIndex) {
|
|
15401
|
-
if (this._super.inError) return;
|
|
15402
|
-
if (this.tablature.bypass(line)) return;
|
|
15403
|
-
var rndrer = new TabRenderer(this, renderer, line, staffIndex);
|
|
15404
|
-
rndrer.doLayout();
|
|
15405
|
-
};
|
|
15406
|
-
function Plugin() {}
|
|
15407
|
-
|
|
15408
|
-
//
|
|
15409
|
-
// Tablature plugin definition
|
|
15410
|
-
//
|
|
15411
|
-
var AbcGuitarTab = function AbcGuitarTab() {
|
|
15412
|
-
return {
|
|
15413
|
-
name: 'GuitarTab',
|
|
15414
|
-
tablature: Plugin
|
|
15415
|
-
};
|
|
15416
|
-
};
|
|
15417
|
-
module.exports = AbcGuitarTab;
|
|
15418
|
-
|
|
15419
|
-
/***/ }),
|
|
15420
|
-
|
|
15421
15754
|
/***/ "./src/tablatures/instruments/string-patterns.js":
|
|
15422
15755
|
/*!*******************************************************!*\
|
|
15423
15756
|
!*** ./src/tablatures/instruments/string-patterns.js ***!
|
|
@@ -15658,6 +15991,17 @@ StringPatterns.prototype.tabInfos = function (plugin) {
|
|
|
15658
15991
|
return '';
|
|
15659
15992
|
};
|
|
15660
15993
|
|
|
15994
|
+
// MAE 27 Nov 2023
|
|
15995
|
+
StringPatterns.prototype.suppress = function (plugin) {
|
|
15996
|
+
var _super = plugin._super;
|
|
15997
|
+
var suppress = _super.params.suppress;
|
|
15998
|
+
if (suppress) {
|
|
15999
|
+
return true;
|
|
16000
|
+
}
|
|
16001
|
+
return false;
|
|
16002
|
+
};
|
|
16003
|
+
// MAE 27 Nov 2023 End
|
|
16004
|
+
|
|
15661
16005
|
/**
|
|
15662
16006
|
* Common patterns for all string instruments
|
|
15663
16007
|
* @param {} plugin
|
|
@@ -16032,16 +16376,43 @@ module.exports = TabNotes;
|
|
|
16032
16376
|
|
|
16033
16377
|
/***/ }),
|
|
16034
16378
|
|
|
16035
|
-
/***/ "./src/tablatures/instruments/
|
|
16036
|
-
|
|
16037
|
-
!*** ./src/tablatures/instruments/
|
|
16038
|
-
|
|
16379
|
+
/***/ "./src/tablatures/instruments/tab-string-patterns.js":
|
|
16380
|
+
/*!***********************************************************!*\
|
|
16381
|
+
!*** ./src/tablatures/instruments/tab-string-patterns.js ***!
|
|
16382
|
+
\***********************************************************/
|
|
16383
|
+
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
16384
|
+
|
|
16385
|
+
var StringPatterns = __webpack_require__(/*! ./string-patterns */ "./src/tablatures/instruments/string-patterns.js");
|
|
16386
|
+
function TabStringPatterns(plugin, defaultTuning) {
|
|
16387
|
+
this.tuning = plugin._super.params.tuning;
|
|
16388
|
+
if (!this.tuning) {
|
|
16389
|
+
this.tuning = defaultTuning;
|
|
16390
|
+
}
|
|
16391
|
+
plugin.tuning = this.tuning;
|
|
16392
|
+
this.strings = new StringPatterns(plugin);
|
|
16393
|
+
}
|
|
16394
|
+
TabStringPatterns.prototype.notesToNumber = function (notes, graces) {
|
|
16395
|
+
var converter = this.strings;
|
|
16396
|
+
return converter.notesToNumber(notes, graces);
|
|
16397
|
+
};
|
|
16398
|
+
TabStringPatterns.prototype.stringToPitch = function (stringNumber) {
|
|
16399
|
+
var converter = this.strings;
|
|
16400
|
+
return converter.stringToPitch(stringNumber);
|
|
16401
|
+
};
|
|
16402
|
+
module.exports = TabStringPatterns;
|
|
16403
|
+
|
|
16404
|
+
/***/ }),
|
|
16405
|
+
|
|
16406
|
+
/***/ "./src/tablatures/instruments/tab-string.js":
|
|
16407
|
+
/*!**************************************************!*\
|
|
16408
|
+
!*** ./src/tablatures/instruments/tab-string.js ***!
|
|
16409
|
+
\**************************************************/
|
|
16039
16410
|
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
16040
16411
|
|
|
16041
|
-
var StringTablature = __webpack_require__(/*!
|
|
16042
|
-
var TabCommon = __webpack_require__(/*!
|
|
16043
|
-
var TabRenderer = __webpack_require__(/*!
|
|
16044
|
-
var
|
|
16412
|
+
var StringTablature = __webpack_require__(/*! ./string-tablature */ "./src/tablatures/instruments/string-tablature.js");
|
|
16413
|
+
var TabCommon = __webpack_require__(/*! ../tab-common */ "./src/tablatures/tab-common.js");
|
|
16414
|
+
var TabRenderer = __webpack_require__(/*! ../tab-renderer */ "./src/tablatures/tab-renderer.js");
|
|
16415
|
+
var TabStringPatterns = __webpack_require__(/*! ./tab-string-patterns */ "./src/tablatures/instruments/tab-string-patterns.js");
|
|
16045
16416
|
|
|
16046
16417
|
/**
|
|
16047
16418
|
* upon init mainly store provided instances for later usage
|
|
@@ -16049,17 +16420,19 @@ var ViolinPatterns = __webpack_require__(/*! ./violin-patterns */ "./src/tablatu
|
|
|
16049
16420
|
* @param {*} tuneNumber the parsed tune AST tree
|
|
16050
16421
|
* @param {*} params complementary args provided to Tablature Plugin
|
|
16051
16422
|
*/
|
|
16052
|
-
Plugin.prototype.init = function (abcTune, tuneNumber, params) {
|
|
16423
|
+
Plugin.prototype.init = function (abcTune, tuneNumber, params, staffNumber, tabSettings) {
|
|
16053
16424
|
var _super = new TabCommon(abcTune, tuneNumber, params);
|
|
16054
16425
|
this.abcTune = abcTune;
|
|
16055
16426
|
this._super = _super;
|
|
16056
16427
|
this.linePitch = 3;
|
|
16057
|
-
this.nbLines =
|
|
16058
|
-
this.isTabBig =
|
|
16428
|
+
this.nbLines = tabSettings.defaultTuning.length;
|
|
16429
|
+
this.isTabBig = tabSettings.isTabBig;
|
|
16430
|
+
this.tabSymbolOffset = tabSettings.tabSymbolOffset;
|
|
16059
16431
|
this.capo = params.capo;
|
|
16060
16432
|
this.transpose = params.visualTranspose;
|
|
16433
|
+
this.hideTabSymbol = params.hideTabSymbol;
|
|
16061
16434
|
this.tablature = new StringTablature(this.nbLines, this.linePitch);
|
|
16062
|
-
var semantics = new
|
|
16435
|
+
var semantics = new TabStringPatterns(this, tabSettings.defaultTuning);
|
|
16063
16436
|
this.semantics = semantics;
|
|
16064
16437
|
};
|
|
16065
16438
|
Plugin.prototype.render = function (renderer, line, staffIndex) {
|
|
@@ -16073,40 +16446,13 @@ function Plugin() {}
|
|
|
16073
16446
|
//
|
|
16074
16447
|
// Tablature plugin definition
|
|
16075
16448
|
//
|
|
16076
|
-
var
|
|
16449
|
+
var AbcStringTab = function AbcStringTab() {
|
|
16077
16450
|
return {
|
|
16078
|
-
name: '
|
|
16451
|
+
name: 'StringTab',
|
|
16079
16452
|
tablature: Plugin
|
|
16080
16453
|
};
|
|
16081
16454
|
};
|
|
16082
|
-
module.exports =
|
|
16083
|
-
|
|
16084
|
-
/***/ }),
|
|
16085
|
-
|
|
16086
|
-
/***/ "./src/tablatures/instruments/violin/violin-patterns.js":
|
|
16087
|
-
/*!**************************************************************!*\
|
|
16088
|
-
!*** ./src/tablatures/instruments/violin/violin-patterns.js ***!
|
|
16089
|
-
\**************************************************************/
|
|
16090
|
-
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
16091
|
-
|
|
16092
|
-
var StringPatterns = __webpack_require__(/*! ../string-patterns */ "./src/tablatures/instruments/string-patterns.js");
|
|
16093
|
-
function ViolinPatterns(plugin) {
|
|
16094
|
-
this.tuning = plugin._super.params.tuning;
|
|
16095
|
-
if (!this.tuning) {
|
|
16096
|
-
this.tuning = ['G,', 'D', 'A', 'e'];
|
|
16097
|
-
}
|
|
16098
|
-
plugin.tuning = this.tuning;
|
|
16099
|
-
this.strings = new StringPatterns(plugin);
|
|
16100
|
-
}
|
|
16101
|
-
ViolinPatterns.prototype.notesToNumber = function (notes, graces) {
|
|
16102
|
-
var converter = this.strings;
|
|
16103
|
-
return converter.notesToNumber(notes, graces);
|
|
16104
|
-
};
|
|
16105
|
-
ViolinPatterns.prototype.stringToPitch = function (stringNumber) {
|
|
16106
|
-
var converter = this.strings;
|
|
16107
|
-
return converter.stringToPitch(stringNumber);
|
|
16108
|
-
};
|
|
16109
|
-
module.exports = ViolinPatterns;
|
|
16455
|
+
module.exports = AbcStringTab;
|
|
16110
16456
|
|
|
16111
16457
|
/***/ }),
|
|
16112
16458
|
|
|
@@ -16175,13 +16521,20 @@ function buildTabAbsolute(plugin, absX, relX) {
|
|
|
16175
16521
|
icon: tabIcon,
|
|
16176
16522
|
Ypos: tabYPos
|
|
16177
16523
|
};
|
|
16178
|
-
|
|
16179
|
-
|
|
16180
|
-
|
|
16181
|
-
|
|
16182
|
-
|
|
16183
|
-
if (
|
|
16184
|
-
|
|
16524
|
+
|
|
16525
|
+
// Offset the TAB symbol position if specified in the tab description
|
|
16526
|
+
tabYPos += plugin.tabSymbolOffset;
|
|
16527
|
+
|
|
16528
|
+
// For tablature like whistle tab where you want the TAB symbol hidden
|
|
16529
|
+
if (!plugin.hideTabSymbol) {
|
|
16530
|
+
var tabAbsolute = new AbsoluteElement(element, 0, 0, "symbol", 0);
|
|
16531
|
+
tabAbsolute.x = absX;
|
|
16532
|
+
var tabRelative = new RelativeElement(tabIcon, 0, 0, 7.5, "tab");
|
|
16533
|
+
tabRelative.x = relX;
|
|
16534
|
+
tabAbsolute.children.push(tabRelative);
|
|
16535
|
+
if (tabAbsolute.abcelem.el_type == 'tab') {
|
|
16536
|
+
tabRelative.pitch = tabYPos;
|
|
16537
|
+
}
|
|
16185
16538
|
}
|
|
16186
16539
|
return tabAbsolute;
|
|
16187
16540
|
}
|
|
@@ -16498,12 +16851,23 @@ function buildTabName(self, dest) {
|
|
|
16498
16851
|
var controller = self.renderer.controller;
|
|
16499
16852
|
var textSize = controller.getTextSize;
|
|
16500
16853
|
var tabName = stringSemantics.tabInfos(self.plugin);
|
|
16501
|
-
var
|
|
16502
|
-
|
|
16503
|
-
|
|
16504
|
-
|
|
16505
|
-
}
|
|
16506
|
-
|
|
16854
|
+
var suppress = stringSemantics.suppress(self.plugin);
|
|
16855
|
+
var doDraw = true;
|
|
16856
|
+
if (suppress) {
|
|
16857
|
+
doDraw = false;
|
|
16858
|
+
}
|
|
16859
|
+
if (doDraw) {
|
|
16860
|
+
var size = textSize.calc(tabName, 'tablabelfont', 'text instrumentname');
|
|
16861
|
+
dest.tabNameInfos = {
|
|
16862
|
+
textSize: {
|
|
16863
|
+
height: size.height,
|
|
16864
|
+
width: size.width
|
|
16865
|
+
},
|
|
16866
|
+
name: tabName
|
|
16867
|
+
};
|
|
16868
|
+
return size.height;
|
|
16869
|
+
}
|
|
16870
|
+
return 0;
|
|
16507
16871
|
}
|
|
16508
16872
|
|
|
16509
16873
|
/**
|
|
@@ -16675,8 +17039,10 @@ TabRenderer.prototype.doLayout = function () {
|
|
|
16675
17039
|
if (ii > 0) tabVoice.duplicate = true;
|
|
16676
17040
|
var nameHeight = buildTabName(this, tabVoice) / spacing.STEP;
|
|
16677
17041
|
nameHeight = Math.max(nameHeight, 1); // If there is no label for the tab line, then there needs to be a little padding
|
|
16678
|
-
|
|
16679
|
-
staffGroup.
|
|
17042
|
+
// This was pushing down the top staff by the tab label height
|
|
17043
|
+
//staffGroup.staffs[this.staffIndex].top += nameHeight;
|
|
17044
|
+
staffGroup.staffs[this.staffIndex].top += 1;
|
|
17045
|
+
staffGroup.height += nameHeight;
|
|
16680
17046
|
tabVoice.staff = staffGroupInfos;
|
|
16681
17047
|
var tabVoiceIndex = voices.length;
|
|
16682
17048
|
voices.splice(voices.length, 0, tabVoice);
|
|
@@ -16822,6 +17188,7 @@ var AbstractEngraver = function AbstractEngraver(getTextSize, tuneNumber, option
|
|
|
16822
17188
|
this.percmap = options.percmap;
|
|
16823
17189
|
this.initialClef = options.initialClef;
|
|
16824
17190
|
this.jazzchords = !!options.jazzchords;
|
|
17191
|
+
this.accentAbove = !!options.accentAbove;
|
|
16825
17192
|
this.germanAlphabet = !!options.germanAlphabet;
|
|
16826
17193
|
this.reset();
|
|
16827
17194
|
};
|
|
@@ -17618,7 +17985,10 @@ AbstractEngraver.prototype.createNote = function (elem, nostem, isSingleLineStaf
|
|
|
17618
17985
|
roomtaken += this.addGraceNotes(elem, voice, abselem, notehead, this.stemHeight * this.voiceScale, this.isBagpipes, roomtaken);
|
|
17619
17986
|
}
|
|
17620
17987
|
if (elem.decoration) {
|
|
17621
|
-
this.
|
|
17988
|
+
// TODO-PER: nostem is true if this is beamed. In that case we don't know where to place the decoration yet so just make a guess. This should be refactored to not place decorations until after the beams are determined.
|
|
17989
|
+
// This should probably be combined with moveDecorations()
|
|
17990
|
+
var bottom = nostem ? Math.min(-3, abselem.bottom - 6) : abselem.bottom;
|
|
17991
|
+
this.decoration.createDecoration(voice, elem.decoration, abselem.top, notehead ? notehead.w : 0, abselem, roomtaken, dir, bottom, elem.positioning, this.hasVocals, this.accentAbove);
|
|
17622
17992
|
}
|
|
17623
17993
|
if (elem.barNumber) {
|
|
17624
17994
|
abselem.addFixed(new RelativeElement(elem.barNumber, -10, 0, 0, {
|
|
@@ -17790,7 +18160,7 @@ AbstractEngraver.prototype.createBarLine = function (voice, elem, isFirstStaff)
|
|
|
17790
18160
|
abselem.addRight(anchor);
|
|
17791
18161
|
}
|
|
17792
18162
|
if (elem.decoration) {
|
|
17793
|
-
this.decoration.createDecoration(voice, elem.decoration, 12, thick ? 3 : 1, abselem, 0, "down", 2, elem.positioning, this.hasVocals);
|
|
18163
|
+
this.decoration.createDecoration(voice, elem.decoration, 12, thick ? 3 : 1, abselem, 0, "down", 2, elem.positioning, this.hasVocals, this.accentAbove);
|
|
17794
18164
|
}
|
|
17795
18165
|
if (thick) {
|
|
17796
18166
|
dx += 4; //3 hardcoded;
|
|
@@ -17860,100 +18230,121 @@ var addChord = function addChord(getTextSize, abselem, elem, roomTaken, roomTake
|
|
|
17860
18230
|
for (var i = 0; i < elem.chord.length; i++) {
|
|
17861
18231
|
var pos = elem.chord[i].position;
|
|
17862
18232
|
var rel_position = elem.chord[i].rel_position;
|
|
17863
|
-
var
|
|
17864
|
-
|
|
17865
|
-
|
|
17866
|
-
|
|
17867
|
-
|
|
17868
|
-
|
|
17869
|
-
|
|
17870
|
-
|
|
17871
|
-
|
|
17872
|
-
|
|
17873
|
-
|
|
17874
|
-
|
|
17875
|
-
|
|
17876
|
-
|
|
17877
|
-
|
|
17878
|
-
|
|
17879
|
-
|
|
17880
|
-
|
|
17881
|
-
|
|
17882
|
-
var
|
|
17883
|
-
|
|
17884
|
-
|
|
17885
|
-
|
|
17886
|
-
|
|
17887
|
-
|
|
17888
|
-
|
|
17889
|
-
|
|
17890
|
-
|
|
17891
|
-
|
|
17892
|
-
|
|
17893
|
-
|
|
17894
|
-
|
|
17895
|
-
|
|
17896
|
-
|
|
17897
|
-
|
|
17898
|
-
|
|
17899
|
-
|
|
17900
|
-
|
|
17901
|
-
|
|
17902
|
-
|
|
17903
|
-
|
|
17904
|
-
|
|
17905
|
-
|
|
17906
|
-
|
|
17907
|
-
|
|
17908
|
-
|
|
18233
|
+
var isAnnotation = pos === "left" || pos === "right" || pos === "below" || pos === "above" || !!rel_position;
|
|
18234
|
+
var font;
|
|
18235
|
+
var klass;
|
|
18236
|
+
if (isAnnotation) {
|
|
18237
|
+
font = 'annotationfont';
|
|
18238
|
+
klass = "abcjs-annotation";
|
|
18239
|
+
} else {
|
|
18240
|
+
font = 'gchordfont';
|
|
18241
|
+
klass = "abcjs-chord";
|
|
18242
|
+
}
|
|
18243
|
+
var attr = getTextSize.attr(font, klass);
|
|
18244
|
+
var name = elem.chord[i].name;
|
|
18245
|
+
var ret;
|
|
18246
|
+
//console.log("chord",name)
|
|
18247
|
+
if (typeof name === "string") {
|
|
18248
|
+
ret = chordString(name, pos, rel_position, isAnnotation, font, klass, attr, getTextSize, abselem, elem, roomTaken, roomTakenRight, noteheadWidth, jazzchords, germanAlphabet);
|
|
18249
|
+
roomTaken = ret.roomTaken;
|
|
18250
|
+
roomTakenRight = ret.roomTakenRight;
|
|
18251
|
+
} else {
|
|
18252
|
+
for (var j = 0; j < name.length; j++) {
|
|
18253
|
+
ret = chordString(name[j].text, pos, rel_position, isAnnotation, font, klass, attr, getTextSize, abselem, elem, roomTaken, roomTakenRight, noteheadWidth, jazzchords, germanAlphabet);
|
|
18254
|
+
roomTaken = ret.roomTaken;
|
|
18255
|
+
roomTakenRight = ret.roomTakenRight;
|
|
18256
|
+
}
|
|
18257
|
+
}
|
|
18258
|
+
}
|
|
18259
|
+
return {
|
|
18260
|
+
roomTaken: roomTaken,
|
|
18261
|
+
roomTakenRight: roomTakenRight
|
|
18262
|
+
};
|
|
18263
|
+
};
|
|
18264
|
+
function chordString(chordString, pos, rel_position, isAnnotation, font, klass, attr, getTextSize, abselem, elem, roomTaken, roomTakenRight, noteheadWidth, jazzchords, germanAlphabet) {
|
|
18265
|
+
var chords = chordString.split("\n");
|
|
18266
|
+
for (var j = chords.length - 1; j >= 0; j--) {
|
|
18267
|
+
// parse these in opposite order because we place them from bottom to top.
|
|
18268
|
+
var chord = chords[j];
|
|
18269
|
+
var x = 0;
|
|
18270
|
+
var y;
|
|
18271
|
+
if (!isAnnotation) chord = translateChord(chord, jazzchords, germanAlphabet);
|
|
18272
|
+
var dim = getTextSize.calc(chord, font, klass);
|
|
18273
|
+
var chordWidth = dim.width;
|
|
18274
|
+
var chordHeight = dim.height / spacing.STEP;
|
|
18275
|
+
switch (pos) {
|
|
18276
|
+
case "left":
|
|
18277
|
+
roomTaken += chordWidth + 7;
|
|
18278
|
+
x = -roomTaken; // TODO-PER: This is just a guess from trial and error
|
|
18279
|
+
y = elem.averagepitch;
|
|
18280
|
+
abselem.addExtra(new RelativeElement(chord, x, chordWidth + 4, y, {
|
|
18281
|
+
type: "text",
|
|
18282
|
+
height: chordHeight,
|
|
18283
|
+
dim: attr,
|
|
18284
|
+
position: "left"
|
|
18285
|
+
}));
|
|
18286
|
+
break;
|
|
18287
|
+
case "right":
|
|
18288
|
+
roomTakenRight += 4;
|
|
18289
|
+
x = roomTakenRight; // TODO-PER: This is just a guess from trial and error
|
|
18290
|
+
y = elem.averagepitch;
|
|
18291
|
+
abselem.addRight(new RelativeElement(chord, x, chordWidth + 4, y, {
|
|
18292
|
+
type: "text",
|
|
18293
|
+
height: chordHeight,
|
|
18294
|
+
dim: attr,
|
|
18295
|
+
position: "right"
|
|
18296
|
+
}));
|
|
18297
|
+
break;
|
|
18298
|
+
case "below":
|
|
18299
|
+
// setting the y-coordinate to undefined for now: it will be overwritten later on, after we figure out what the highest element on the line is.
|
|
18300
|
+
abselem.addRight(new RelativeElement(chord, 0, 0, undefined, {
|
|
18301
|
+
type: "text",
|
|
18302
|
+
position: "below",
|
|
18303
|
+
height: chordHeight,
|
|
18304
|
+
dim: attr,
|
|
18305
|
+
realWidth: chordWidth
|
|
18306
|
+
}));
|
|
18307
|
+
break;
|
|
18308
|
+
case "above":
|
|
18309
|
+
// setting the y-coordinate to undefined for now: it will be overwritten later on, after we figure out what the highest element on the line is.
|
|
18310
|
+
abselem.addRight(new RelativeElement(chord, 0, 0, undefined, {
|
|
18311
|
+
type: "text",
|
|
18312
|
+
position: "above",
|
|
18313
|
+
height: chordHeight,
|
|
18314
|
+
dim: attr,
|
|
18315
|
+
realWidth: chordWidth
|
|
18316
|
+
}));
|
|
18317
|
+
break;
|
|
18318
|
+
default:
|
|
18319
|
+
if (rel_position) {
|
|
18320
|
+
var relPositionY = rel_position.y + 3 * spacing.STEP; // TODO-PER: this is a fudge factor to make it line up with abcm2ps
|
|
18321
|
+
abselem.addRight(new RelativeElement(chord, x + rel_position.x, 0, elem.minpitch + relPositionY / spacing.STEP, {
|
|
18322
|
+
position: "relative",
|
|
17909
18323
|
type: "text",
|
|
17910
|
-
position: "below",
|
|
17911
18324
|
height: chordHeight,
|
|
17912
|
-
dim: attr
|
|
17913
|
-
realWidth: chordWidth
|
|
18325
|
+
dim: attr
|
|
17914
18326
|
}));
|
|
17915
|
-
|
|
17916
|
-
case "above":
|
|
18327
|
+
} else {
|
|
17917
18328
|
// setting the y-coordinate to undefined for now: it will be overwritten later on, after we figure out what the highest element on the line is.
|
|
17918
|
-
|
|
17919
|
-
|
|
17920
|
-
|
|
17921
|
-
|
|
17922
|
-
|
|
17923
|
-
|
|
17924
|
-
}));
|
|
17925
|
-
break;
|
|
17926
|
-
default:
|
|
17927
|
-
if (rel_position) {
|
|
17928
|
-
var relPositionY = rel_position.y + 3 * spacing.STEP; // TODO-PER: this is a fudge factor to make it line up with abcm2ps
|
|
17929
|
-
abselem.addRight(new RelativeElement(chord, x + rel_position.x, 0, elem.minpitch + relPositionY / spacing.STEP, {
|
|
17930
|
-
position: "relative",
|
|
17931
|
-
type: "text",
|
|
18329
|
+
var pos2 = 'above';
|
|
18330
|
+
if (elem.positioning && elem.positioning.chordPosition) pos2 = elem.positioning.chordPosition;
|
|
18331
|
+
if (pos2 !== 'hidden') {
|
|
18332
|
+
abselem.addCentered(new RelativeElement(chord, noteheadWidth / 2, chordWidth, undefined, {
|
|
18333
|
+
type: "chord",
|
|
18334
|
+
position: pos2,
|
|
17932
18335
|
height: chordHeight,
|
|
17933
|
-
dim: attr
|
|
18336
|
+
dim: attr,
|
|
18337
|
+
realWidth: chordWidth
|
|
17934
18338
|
}));
|
|
17935
|
-
} else {
|
|
17936
|
-
// setting the y-coordinate to undefined for now: it will be overwritten later on, after we figure out what the highest element on the line is.
|
|
17937
|
-
var pos2 = 'above';
|
|
17938
|
-
if (elem.positioning && elem.positioning.chordPosition) pos2 = elem.positioning.chordPosition;
|
|
17939
|
-
if (pos2 !== 'hidden') {
|
|
17940
|
-
abselem.addCentered(new RelativeElement(chord, noteheadWidth / 2, chordWidth, undefined, {
|
|
17941
|
-
type: "chord",
|
|
17942
|
-
position: pos2,
|
|
17943
|
-
height: chordHeight,
|
|
17944
|
-
dim: attr,
|
|
17945
|
-
realWidth: chordWidth
|
|
17946
|
-
}));
|
|
17947
|
-
}
|
|
17948
18339
|
}
|
|
17949
|
-
|
|
18340
|
+
}
|
|
17950
18341
|
}
|
|
17951
18342
|
}
|
|
17952
18343
|
return {
|
|
17953
18344
|
roomTaken: roomTaken,
|
|
17954
18345
|
roomTakenRight: roomTakenRight
|
|
17955
18346
|
};
|
|
17956
|
-
}
|
|
18347
|
+
}
|
|
17957
18348
|
module.exports = addChord;
|
|
17958
18349
|
|
|
17959
18350
|
/***/ }),
|
|
@@ -17982,10 +18373,11 @@ function addTextIf(rows, params, getTextSize) {
|
|
|
17982
18373
|
font: params.font,
|
|
17983
18374
|
anchor: params.anchor,
|
|
17984
18375
|
startChar: params.info.startChar,
|
|
17985
|
-
endChar: params.info.endChar
|
|
18376
|
+
endChar: params.info.endChar,
|
|
18377
|
+
'dominant-baseline': params['dominant-baseline']
|
|
17986
18378
|
};
|
|
17987
18379
|
if (params.absElemType) attr.absElemType = params.absElemType;
|
|
17988
|
-
if (!params.inGroup) attr.klass = params.klass;
|
|
18380
|
+
if (!params.inGroup && params.klass) attr.klass = params.klass;
|
|
17989
18381
|
if (params.name) attr.name = params.name;
|
|
17990
18382
|
rows.push(attr);
|
|
17991
18383
|
// If there are blank lines they won't be counted by getTextSize, so just get the height of one line and multiply
|
|
@@ -18441,10 +18833,10 @@ var Decoration = function Decoration() {
|
|
|
18441
18833
|
this.minTop = 12; // TODO-PER: this is assuming a 5-line staff. Pass that info in.
|
|
18442
18834
|
this.minBottom = 0;
|
|
18443
18835
|
};
|
|
18444
|
-
var closeDecoration = function closeDecoration(voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch) {
|
|
18836
|
+
var closeDecoration = function closeDecoration(voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, accentAbove) {
|
|
18445
18837
|
var yPos;
|
|
18446
18838
|
for (var i = 0; i < decoration.length; i++) {
|
|
18447
|
-
if (decoration[i] === "staccato" || decoration[i] === "tenuto" || decoration[i] === "accent") {
|
|
18839
|
+
if (decoration[i] === "staccato" || decoration[i] === "tenuto" || decoration[i] === "accent" && !accentAbove) {
|
|
18448
18840
|
var symbol = "scripts." + decoration[i];
|
|
18449
18841
|
if (decoration[i] === "accent") symbol = "scripts.sforzato";
|
|
18450
18842
|
if (yPos === undefined) yPos = dir === "down" ? pitch + 2 : minPitch - 2;else yPos = dir === "down" ? yPos + 2 : yPos - 2;
|
|
@@ -18553,7 +18945,7 @@ var compoundDecoration = function compoundDecoration(decoration, pitch, width, a
|
|
|
18553
18945
|
}
|
|
18554
18946
|
}
|
|
18555
18947
|
};
|
|
18556
|
-
var stackedDecoration = function stackedDecoration(decoration, width, abselem, yPos, positioning, minTop, minBottom) {
|
|
18948
|
+
var stackedDecoration = function stackedDecoration(decoration, width, abselem, yPos, positioning, minTop, minBottom, accentAbove) {
|
|
18557
18949
|
function incrementPlacement(placement, height) {
|
|
18558
18950
|
if (placement === 'above') yPos.above += height;else yPos.below -= height;
|
|
18559
18951
|
}
|
|
@@ -18591,7 +18983,8 @@ var stackedDecoration = function stackedDecoration(decoration, width, abselem, y
|
|
|
18591
18983
|
y = placement === 'above' ? y + height / 2 : y - height / 2; // Center the element vertically.
|
|
18592
18984
|
abselem.addFixedX(new RelativeElement(symbol, deltaX, glyphs.getSymbolWidth(symbol), y, {
|
|
18593
18985
|
klass: 'ornament',
|
|
18594
|
-
thickness: glyphs.symbolHeightInPitches(symbol)
|
|
18986
|
+
thickness: glyphs.symbolHeightInPitches(symbol),
|
|
18987
|
+
position: placement
|
|
18595
18988
|
}));
|
|
18596
18989
|
incrementPlacement(placement, height);
|
|
18597
18990
|
}
|
|
@@ -18692,6 +19085,12 @@ var stackedDecoration = function stackedDecoration(decoration, width, abselem, y
|
|
|
18692
19085
|
case "mark":
|
|
18693
19086
|
abselem.klass = "mark";
|
|
18694
19087
|
break;
|
|
19088
|
+
case "accent":
|
|
19089
|
+
if (accentAbove) {
|
|
19090
|
+
symbolDecoration("scripts.sforzato", positioning);
|
|
19091
|
+
hasOne = true;
|
|
19092
|
+
}
|
|
19093
|
+
break;
|
|
18695
19094
|
}
|
|
18696
19095
|
}
|
|
18697
19096
|
return hasOne;
|
|
@@ -18766,7 +19165,7 @@ Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, p
|
|
|
18766
19165
|
voice.addOther(new GlissandoElem(glissando.start, glissando.stop));
|
|
18767
19166
|
}
|
|
18768
19167
|
};
|
|
18769
|
-
Decoration.prototype.createDecoration = function (voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, positioning, hasVocals) {
|
|
19168
|
+
Decoration.prototype.createDecoration = function (voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, positioning, hasVocals, accentAbove) {
|
|
18770
19169
|
if (!positioning) positioning = {
|
|
18771
19170
|
ornamentPosition: 'above',
|
|
18772
19171
|
volumePosition: hasVocals ? 'above' : 'below',
|
|
@@ -18778,14 +19177,15 @@ Decoration.prototype.createDecoration = function (voice, decoration, pitch, widt
|
|
|
18778
19177
|
compoundDecoration(decoration, pitch, width, abselem, dir);
|
|
18779
19178
|
|
|
18780
19179
|
// treat staccato, accent, and tenuto first (may need to shift other markers)
|
|
18781
|
-
var yPos = closeDecoration(voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch);
|
|
19180
|
+
var yPos = closeDecoration(voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, accentAbove);
|
|
18782
19181
|
// yPos is an object containing 'above' and 'below'. That is the placement of the next symbol on either side.
|
|
18783
19182
|
|
|
18784
19183
|
yPos.above = Math.max(yPos.above, this.minTop);
|
|
18785
|
-
|
|
18786
|
-
|
|
18787
|
-
|
|
18788
|
-
|
|
19184
|
+
yPos.below = Math.min(yPos.below, minPitch);
|
|
19185
|
+
var hasOne = stackedDecoration(decoration, width, abselem, yPos, positioning.ornamentPosition, this.minTop, minPitch, accentAbove);
|
|
19186
|
+
//if (hasOne) {
|
|
19187
|
+
// abselem.top = Math.max(yPos.above + 3, abselem.top); // TODO-PER: Not sure why we need this fudge factor.
|
|
19188
|
+
//}
|
|
18789
19189
|
leftDecoration(decoration, abselem, roomtaken);
|
|
18790
19190
|
};
|
|
18791
19191
|
module.exports = Decoration;
|
|
@@ -19145,94 +19545,109 @@ module.exports = BeamElem;
|
|
|
19145
19545
|
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
19146
19546
|
|
|
19147
19547
|
var addTextIf = __webpack_require__(/*! ../add-text-if */ "./src/write/creation/add-text-if.js");
|
|
19148
|
-
|
|
19548
|
+
var richText = __webpack_require__(/*! ./rich-text */ "./src/write/creation/elements/rich-text.js");
|
|
19549
|
+
function BottomText(metaText, width, isPrint, paddingLeft, spacing, shouldAddClasses, getTextSize) {
|
|
19149
19550
|
this.rows = [];
|
|
19150
|
-
if (metaText.unalignedWords && metaText.unalignedWords.length > 0) this.unalignedWords(metaText.unalignedWords, paddingLeft, spacing, getTextSize);
|
|
19151
|
-
this.extraText(metaText, paddingLeft, spacing, getTextSize);
|
|
19551
|
+
if (metaText.unalignedWords && metaText.unalignedWords.length > 0) this.unalignedWords(metaText.unalignedWords, paddingLeft, spacing, shouldAddClasses, getTextSize);
|
|
19552
|
+
this.extraText(metaText, paddingLeft, spacing, shouldAddClasses, getTextSize);
|
|
19152
19553
|
if (metaText.footer && isPrint) this.footer(metaText.footer, width, paddingLeft, getTextSize);
|
|
19153
19554
|
}
|
|
19154
|
-
BottomText.prototype.unalignedWords = function (unalignedWords,
|
|
19155
|
-
var klass = '
|
|
19555
|
+
BottomText.prototype.unalignedWords = function (unalignedWords, marginLeft, spacing, shouldAddClasses, getTextSize) {
|
|
19556
|
+
var klass = shouldAddClasses ? 'abcjs-unaligned-words' : '';
|
|
19156
19557
|
var defFont = 'wordsfont';
|
|
19157
|
-
this.rows.push({
|
|
19158
|
-
startGroup: "unalignedWords",
|
|
19159
|
-
klass: 'abcjs-meta-bottom abcjs-unaligned-words',
|
|
19160
|
-
name: "words"
|
|
19161
|
-
});
|
|
19162
19558
|
var space = getTextSize.calc("i", defFont, klass);
|
|
19163
19559
|
this.rows.push({
|
|
19164
19560
|
move: spacing.words
|
|
19165
19561
|
});
|
|
19166
|
-
|
|
19167
|
-
|
|
19168
|
-
|
|
19169
|
-
|
|
19170
|
-
|
|
19171
|
-
|
|
19172
|
-
|
|
19562
|
+
addMultiLine(this.rows, '', unalignedWords, marginLeft, defFont, "unalignedWords", "unalignedWords", klass, "unalignedWords", spacing, shouldAddClasses, getTextSize);
|
|
19563
|
+
this.rows.push({
|
|
19564
|
+
move: space.height
|
|
19565
|
+
});
|
|
19566
|
+
};
|
|
19567
|
+
function addSingleLine(rows, preface, text, marginLeft, klass, shouldAddClasses, getTextSize) {
|
|
19568
|
+
if (text) {
|
|
19569
|
+
if (preface) {
|
|
19570
|
+
if (typeof text === 'string') text = preface + text;else text = [{
|
|
19571
|
+
text: preface
|
|
19572
|
+
}].concat(text);
|
|
19573
|
+
}
|
|
19574
|
+
klass = shouldAddClasses ? 'abcjs-extra-text ' + klass : '';
|
|
19575
|
+
richText(rows, text, 'historyfont', klass, "description", marginLeft, {
|
|
19576
|
+
absElemType: "extraText",
|
|
19577
|
+
anchor: 'start'
|
|
19578
|
+
}, getTextSize);
|
|
19579
|
+
}
|
|
19580
|
+
}
|
|
19581
|
+
function addMultiLine(rows, preface, content, marginLeft, defFont, absElemType, groupName, klass, name, spacing, shouldAddClasses, getTextSize) {
|
|
19582
|
+
if (content) {
|
|
19583
|
+
klass = shouldAddClasses ? 'abcjs-extra-text ' + klass : '';
|
|
19584
|
+
var size = getTextSize.calc("A", defFont, klass);
|
|
19585
|
+
if (typeof content === 'string') {
|
|
19586
|
+
if (preface) content = preface + "\n" + content;
|
|
19587
|
+
addTextIf(rows, {
|
|
19588
|
+
marginLeft: marginLeft,
|
|
19589
|
+
text: content,
|
|
19173
19590
|
font: defFont,
|
|
19174
|
-
|
|
19175
|
-
|
|
19176
|
-
|
|
19591
|
+
absElemType: "extraText",
|
|
19592
|
+
name: name,
|
|
19593
|
+
'dominant-baseline': 'middle',
|
|
19594
|
+
klass: klass
|
|
19177
19595
|
}, getTextSize);
|
|
19596
|
+
//rows.push({move: size.height*3/4})
|
|
19178
19597
|
} else {
|
|
19179
|
-
|
|
19180
|
-
|
|
19181
|
-
|
|
19182
|
-
|
|
19183
|
-
|
|
19184
|
-
|
|
19185
|
-
|
|
19186
|
-
|
|
19187
|
-
|
|
19598
|
+
rows.push({
|
|
19599
|
+
startGroup: groupName,
|
|
19600
|
+
klass: klass,
|
|
19601
|
+
name: name
|
|
19602
|
+
});
|
|
19603
|
+
rows.push({
|
|
19604
|
+
move: spacing.info
|
|
19605
|
+
});
|
|
19606
|
+
if (preface) {
|
|
19607
|
+
addTextIf(rows, {
|
|
19608
|
+
marginLeft: marginLeft,
|
|
19609
|
+
text: preface,
|
|
19610
|
+
font: defFont,
|
|
19611
|
+
absElemType: "extraText",
|
|
19612
|
+
name: name,
|
|
19613
|
+
'dominant-baseline': 'middle'
|
|
19614
|
+
}, getTextSize);
|
|
19615
|
+
rows.push({
|
|
19616
|
+
move: size.height * 3 / 4
|
|
19617
|
+
});
|
|
19618
|
+
}
|
|
19619
|
+
for (var j = 0; j < content.length; j++) {
|
|
19620
|
+
richText(rows, content[j], defFont, '', name, marginLeft, {
|
|
19188
19621
|
anchor: 'start'
|
|
19622
|
+
}, getTextSize);
|
|
19623
|
+
// TODO-PER: Hack! the string and rich lines should have used up the same amount of space without this.
|
|
19624
|
+
if (j < content.length - 1 && typeof content[j] === 'string' && typeof content[j + 1] !== 'string') rows.push({
|
|
19625
|
+
move: size.height * 3 / 4
|
|
19189
19626
|
});
|
|
19190
|
-
var size = getTextSize.calc(thisWord.text, defFont, klass);
|
|
19191
|
-
largestY = Math.max(largestY, size.height);
|
|
19192
|
-
offsetX += size.width;
|
|
19193
|
-
// If the phrase ends in a space, then that is not counted in the width, so we need to add that in ourselves.
|
|
19194
|
-
if (thisWord.text[thisWord.text.length - 1] === ' ') {
|
|
19195
|
-
offsetX += space.width;
|
|
19196
|
-
}
|
|
19197
19627
|
}
|
|
19198
|
-
|
|
19199
|
-
|
|
19628
|
+
rows.push({
|
|
19629
|
+
endGroup: groupName,
|
|
19630
|
+
absElemType: absElemType,
|
|
19631
|
+
startChar: -1,
|
|
19632
|
+
endChar: -1,
|
|
19633
|
+
name: name
|
|
19634
|
+
});
|
|
19635
|
+
rows.push({
|
|
19636
|
+
move: size.height
|
|
19200
19637
|
});
|
|
19201
19638
|
}
|
|
19202
19639
|
}
|
|
19203
|
-
|
|
19204
|
-
|
|
19205
|
-
|
|
19206
|
-
this.rows.
|
|
19207
|
-
|
|
19208
|
-
|
|
19209
|
-
|
|
19210
|
-
|
|
19211
|
-
|
|
19212
|
-
|
|
19213
|
-
|
|
19214
|
-
BottomText.prototype.extraText = function (metaText, marginLeft, spacing, getTextSize) {
|
|
19215
|
-
var extraText = "";
|
|
19216
|
-
if (metaText.book) extraText += "Book: " + metaText.book + "\n";
|
|
19217
|
-
if (metaText.source) extraText += "Source: " + metaText.source + "\n";
|
|
19218
|
-
if (metaText.discography) extraText += "Discography: " + metaText.discography + "\n";
|
|
19219
|
-
if (metaText.notes) extraText += "Notes: " + metaText.notes + "\n";
|
|
19220
|
-
if (metaText.transcription) extraText += "Transcription: " + metaText.transcription + "\n";
|
|
19221
|
-
if (metaText.history) extraText += "History: " + metaText.history + "\n";
|
|
19222
|
-
if (metaText['abc-copyright']) extraText += "Copyright: " + metaText['abc-copyright'] + "\n";
|
|
19223
|
-
if (metaText['abc-creator']) extraText += "Creator: " + metaText['abc-creator'] + "\n";
|
|
19224
|
-
if (metaText['abc-edited-by']) extraText += "Edited By: " + metaText['abc-edited-by'] + "\n";
|
|
19225
|
-
if (extraText.length > 0) {
|
|
19226
|
-
addTextIf(this.rows, {
|
|
19227
|
-
marginLeft: marginLeft,
|
|
19228
|
-
text: extraText,
|
|
19229
|
-
font: 'historyfont',
|
|
19230
|
-
klass: 'meta-bottom extra-text',
|
|
19231
|
-
marginTop: spacing.info,
|
|
19232
|
-
absElemType: "extraText",
|
|
19233
|
-
name: "description"
|
|
19234
|
-
}, getTextSize);
|
|
19235
|
-
}
|
|
19640
|
+
}
|
|
19641
|
+
BottomText.prototype.extraText = function (metaText, marginLeft, spacing, shouldAddClasses, getTextSize) {
|
|
19642
|
+
addSingleLine(this.rows, "Book: ", metaText.book, marginLeft, 'abcjs-book', shouldAddClasses, getTextSize);
|
|
19643
|
+
addSingleLine(this.rows, "Source: ", metaText.source, marginLeft, 'abcjs-source', shouldAddClasses, getTextSize);
|
|
19644
|
+
addSingleLine(this.rows, "Discography: ", metaText.discography, marginLeft, 'abcjs-discography', shouldAddClasses, getTextSize);
|
|
19645
|
+
addMultiLine(this.rows, 'Notes:', metaText.notes, marginLeft, 'historyfont', "extraText", "notes", 'abcjs-notes', "description", spacing, shouldAddClasses, getTextSize);
|
|
19646
|
+
addSingleLine(this.rows, "Transcription: ", metaText.transcription, marginLeft, 'abcjs-transcription', shouldAddClasses, getTextSize);
|
|
19647
|
+
addMultiLine(this.rows, "History:", metaText.history, marginLeft, 'historyfont', "extraText", "history", 'abcjs-history', "description", spacing, shouldAddClasses, getTextSize);
|
|
19648
|
+
addSingleLine(this.rows, "Copyright: ", metaText['abc-copyright'], marginLeft, 'abcjs-copyright', shouldAddClasses, getTextSize);
|
|
19649
|
+
addSingleLine(this.rows, "Creator: ", metaText['abc-creator'], marginLeft, 'abcjs-creator', shouldAddClasses, getTextSize);
|
|
19650
|
+
addSingleLine(this.rows, "Edited By: ", metaText['abc-edited-by'], marginLeft, 'abcjs-edited-by', shouldAddClasses, getTextSize);
|
|
19236
19651
|
};
|
|
19237
19652
|
BottomText.prototype.footer = function (footer, width, paddingLeft, getTextSize) {
|
|
19238
19653
|
var klass = 'header meta-bottom';
|
|
@@ -19574,6 +19989,76 @@ module.exports = RelativeElement;
|
|
|
19574
19989
|
|
|
19575
19990
|
/***/ }),
|
|
19576
19991
|
|
|
19992
|
+
/***/ "./src/write/creation/elements/rich-text.js":
|
|
19993
|
+
/*!**************************************************!*\
|
|
19994
|
+
!*** ./src/write/creation/elements/rich-text.js ***!
|
|
19995
|
+
\**************************************************/
|
|
19996
|
+
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
19997
|
+
|
|
19998
|
+
var addTextIf = __webpack_require__(/*! ../add-text-if */ "./src/write/creation/add-text-if.js");
|
|
19999
|
+
function richText(rows, str, defFont, klass, name, paddingLeft, attr, getTextSize) {
|
|
20000
|
+
var space = getTextSize.calc("i", defFont, klass);
|
|
20001
|
+
if (str === '') {
|
|
20002
|
+
rows.push({
|
|
20003
|
+
move: space.height
|
|
20004
|
+
});
|
|
20005
|
+
} else {
|
|
20006
|
+
if (typeof str === 'string') {
|
|
20007
|
+
addTextIf(rows, {
|
|
20008
|
+
marginLeft: paddingLeft,
|
|
20009
|
+
text: str,
|
|
20010
|
+
font: defFont,
|
|
20011
|
+
klass: klass,
|
|
20012
|
+
marginTop: attr.marginTop,
|
|
20013
|
+
anchor: attr.anchor,
|
|
20014
|
+
absElemType: attr.absElemType,
|
|
20015
|
+
info: attr.info,
|
|
20016
|
+
name: name
|
|
20017
|
+
}, getTextSize);
|
|
20018
|
+
return;
|
|
20019
|
+
}
|
|
20020
|
+
if (attr.marginTop) rows.push({
|
|
20021
|
+
move: attr.marginTop
|
|
20022
|
+
});
|
|
20023
|
+
var largestY = 0;
|
|
20024
|
+
var gap = 0;
|
|
20025
|
+
var row = {
|
|
20026
|
+
left: paddingLeft,
|
|
20027
|
+
anchor: attr.anchor,
|
|
20028
|
+
phrases: []
|
|
20029
|
+
};
|
|
20030
|
+
if (klass) row.klass = klass;
|
|
20031
|
+
rows.push(row);
|
|
20032
|
+
for (var k = 0; k < str.length; k++) {
|
|
20033
|
+
var thisWord = str[k];
|
|
20034
|
+
var font = thisWord.font ? thisWord.font : getTextSize.attr(defFont, klass).font;
|
|
20035
|
+
var phrase = {
|
|
20036
|
+
content: thisWord.text
|
|
20037
|
+
};
|
|
20038
|
+
if (font) phrase.attrs = {
|
|
20039
|
+
"font-family": getTextSize.getFamily(font.face),
|
|
20040
|
+
"font-size": font.size,
|
|
20041
|
+
"font-weight": font.weight,
|
|
20042
|
+
"font-style": font.style,
|
|
20043
|
+
"font-decoration": font.decoration
|
|
20044
|
+
};
|
|
20045
|
+
//if (thisWord.text) {
|
|
20046
|
+
row.phrases.push(phrase);
|
|
20047
|
+
var size = getTextSize.calc(thisWord.text, font, klass);
|
|
20048
|
+
largestY = Math.max(largestY, size.height);
|
|
20049
|
+
if (thisWord.text[thisWord.text.length - 1] === ' ') {
|
|
20050
|
+
gap = space.width;
|
|
20051
|
+
}
|
|
20052
|
+
}
|
|
20053
|
+
rows.push({
|
|
20054
|
+
move: largestY
|
|
20055
|
+
});
|
|
20056
|
+
}
|
|
20057
|
+
}
|
|
20058
|
+
module.exports = richText;
|
|
20059
|
+
|
|
20060
|
+
/***/ }),
|
|
20061
|
+
|
|
19577
20062
|
/***/ "./src/write/creation/elements/separator.js":
|
|
19578
20063
|
/*!**************************************************!*\
|
|
19579
20064
|
!*** ./src/write/creation/elements/separator.js ***!
|
|
@@ -19966,6 +20451,23 @@ TieElem.prototype.calcSlurY = function () {
|
|
|
19966
20451
|
this.endY = midPoint;
|
|
19967
20452
|
this.endX += Math.round(this.anchor2.w / 2); // When going to the middle of the stem, bump the line to the right a little bit to make it look right.
|
|
19968
20453
|
} else this.endY = this.above && beamInterferes ? this.anchor2.highestVert : this.anchor2.pitch;
|
|
20454
|
+
if (this.anchor1.scalex === 1) {
|
|
20455
|
+
// Need a way to tell if this is a grace note - if so then keep the slur as close as possible. TODO-PER-HACK: this should be more declaratively determined.
|
|
20456
|
+
var hasBeam1 = !!this.anchor1.parent.beam;
|
|
20457
|
+
var hasBeam2 = !!this.anchor2.parent.beam;
|
|
20458
|
+
if (hasBeam1) {
|
|
20459
|
+
var isLastInBeam = this.anchor1.parent === this.anchor1.parent.beam.elems[this.anchor1.parent.beam.elems.length - 1];
|
|
20460
|
+
if (!isLastInBeam) {
|
|
20461
|
+
if (this.above) this.startY = this.anchor1.parent.fixed.t;else this.startY = this.anchor1.parent.fixed.b;
|
|
20462
|
+
}
|
|
20463
|
+
}
|
|
20464
|
+
if (hasBeam2) {
|
|
20465
|
+
var isFirstInBeam = this.anchor2.parent === this.anchor2.parent.beam.elems[0];
|
|
20466
|
+
if (!isFirstInBeam) {
|
|
20467
|
+
if (this.above) this.endY = this.anchor2.parent.fixed.t;else this.endY = this.anchor2.parent.fixed.b;
|
|
20468
|
+
}
|
|
20469
|
+
}
|
|
20470
|
+
}
|
|
19969
20471
|
} else if (this.anchor1) {
|
|
19970
20472
|
this.startY = this.endY = this.anchor1.pitch;
|
|
19971
20473
|
} else if (this.anchor2) {
|
|
@@ -19998,7 +20500,8 @@ module.exports = TieElem;
|
|
|
19998
20500
|
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
19999
20501
|
|
|
20000
20502
|
var addTextIf = __webpack_require__(/*! ../add-text-if */ "./src/write/creation/add-text-if.js");
|
|
20001
|
-
|
|
20503
|
+
var richText = __webpack_require__(/*! ./rich-text */ "./src/write/creation/elements/rich-text.js");
|
|
20504
|
+
function TopText(metaText, metaTextInfo, formatting, lines, width, isPrint, paddingLeft, spacing, shouldAddClasses, getTextSize) {
|
|
20002
20505
|
this.rows = [];
|
|
20003
20506
|
if (metaText.header && isPrint) {
|
|
20004
20507
|
// Note: whether there is a header or not doesn't change any other positioning, so this doesn't change the Y-coordinate.
|
|
@@ -20043,31 +20546,23 @@ function TopText(metaText, metaTextInfo, formatting, lines, width, isPrint, padd
|
|
|
20043
20546
|
var tAnchor = formatting.titleleft ? 'start' : 'middle';
|
|
20044
20547
|
var tLeft = formatting.titleleft ? paddingLeft : paddingLeft + width / 2;
|
|
20045
20548
|
if (metaText.title) {
|
|
20046
|
-
|
|
20047
|
-
|
|
20048
|
-
text: metaText.title,
|
|
20049
|
-
font: 'titlefont',
|
|
20050
|
-
klass: 'title meta-top',
|
|
20549
|
+
var klass = shouldAddClasses ? 'abcjs-title' : '';
|
|
20550
|
+
richText(this.rows, metaText.title, "titlefont", klass, 'title', tLeft, {
|
|
20051
20551
|
marginTop: spacing.title,
|
|
20052
20552
|
anchor: tAnchor,
|
|
20053
20553
|
absElemType: "title",
|
|
20054
|
-
info: metaTextInfo.title
|
|
20055
|
-
name: "title"
|
|
20554
|
+
info: metaTextInfo.title
|
|
20056
20555
|
}, getTextSize);
|
|
20057
20556
|
}
|
|
20058
20557
|
if (lines.length) {
|
|
20059
20558
|
var index = 0;
|
|
20060
20559
|
while (index < lines.length && lines[index].subtitle) {
|
|
20061
|
-
|
|
20062
|
-
|
|
20063
|
-
text: lines[index].subtitle.text,
|
|
20064
|
-
font: 'subtitlefont',
|
|
20065
|
-
klass: 'text meta-top subtitle',
|
|
20560
|
+
var klass = shouldAddClasses ? 'abcjs-text abcjs-subtitle' : '';
|
|
20561
|
+
richText(this.rows, lines[index].subtitle.text, "subtitlefont", klass, 'subtitle', tLeft, {
|
|
20066
20562
|
marginTop: spacing.subtitle,
|
|
20067
20563
|
anchor: tAnchor,
|
|
20068
20564
|
absElemType: "subtitle",
|
|
20069
|
-
info: lines[index].subtitle
|
|
20070
|
-
name: "subtitle"
|
|
20565
|
+
info: lines[index].subtitle
|
|
20071
20566
|
}, getTextSize);
|
|
20072
20567
|
index++;
|
|
20073
20568
|
}
|
|
@@ -20078,54 +20573,68 @@ function TopText(metaText, metaTextInfo, formatting, lines, width, isPrint, padd
|
|
|
20078
20573
|
});
|
|
20079
20574
|
if (metaText.rhythm && metaText.rhythm.length > 0) {
|
|
20080
20575
|
var noMove = !!(metaText.composer || metaText.origin);
|
|
20576
|
+
var klass = shouldAddClasses ? 'abcjs-rhythm' : '';
|
|
20081
20577
|
addTextIf(this.rows, {
|
|
20082
20578
|
marginLeft: paddingLeft,
|
|
20083
20579
|
text: metaText.rhythm,
|
|
20084
20580
|
font: 'infofont',
|
|
20085
|
-
klass:
|
|
20581
|
+
klass: klass,
|
|
20086
20582
|
absElemType: "rhythm",
|
|
20087
20583
|
noMove: noMove,
|
|
20088
20584
|
info: metaTextInfo.rhythm,
|
|
20089
20585
|
name: "rhythm"
|
|
20090
20586
|
}, getTextSize);
|
|
20091
20587
|
}
|
|
20092
|
-
var
|
|
20093
|
-
if (metaText.composer
|
|
20094
|
-
if (metaText.origin
|
|
20095
|
-
|
|
20096
|
-
|
|
20097
|
-
|
|
20098
|
-
|
|
20099
|
-
|
|
20100
|
-
|
|
20588
|
+
var hasSimpleComposerLine = true;
|
|
20589
|
+
if (metaText.composer && typeof metaText.composer !== 'string') hasSimpleComposerLine = false;
|
|
20590
|
+
if (metaText.origin && typeof metaText.origin !== 'string') hasSimpleComposerLine = false;
|
|
20591
|
+
var composerLine = metaText.composer ? metaText.composer : '';
|
|
20592
|
+
if (metaText.origin) {
|
|
20593
|
+
if (typeof composerLine === 'string' && typeof metaText.origin === 'string') composerLine += ' (' + metaText.origin + ')';else if (typeof composerLine === 'string' && typeof metaText.origin !== 'string') {
|
|
20594
|
+
composerLine = [{
|
|
20595
|
+
text: composerLine
|
|
20596
|
+
}];
|
|
20597
|
+
composerLine.push({
|
|
20598
|
+
text: " ("
|
|
20599
|
+
});
|
|
20600
|
+
composerLine = composerLine.concat(metaText.origin);
|
|
20601
|
+
composerLine.push({
|
|
20602
|
+
text: ")"
|
|
20603
|
+
});
|
|
20604
|
+
} else {
|
|
20605
|
+
composerLine.push({
|
|
20606
|
+
text: " ("
|
|
20607
|
+
});
|
|
20608
|
+
composerLine = composerLine.concat(metaText.origin);
|
|
20609
|
+
composerLine.push({
|
|
20610
|
+
text: ")"
|
|
20611
|
+
});
|
|
20612
|
+
}
|
|
20613
|
+
}
|
|
20614
|
+
if (composerLine) {
|
|
20615
|
+
var klass = shouldAddClasses ? 'abcjs-composer' : '';
|
|
20616
|
+
richText(this.rows, composerLine, 'composerfont', klass, "composer", paddingLeft + width, {
|
|
20101
20617
|
anchor: "end",
|
|
20102
20618
|
absElemType: "composer",
|
|
20103
20619
|
info: metaTextInfo.composer,
|
|
20104
|
-
|
|
20620
|
+
ingroup: true
|
|
20105
20621
|
}, getTextSize);
|
|
20106
20622
|
}
|
|
20107
20623
|
}
|
|
20108
20624
|
if (metaText.author && metaText.author.length > 0) {
|
|
20109
|
-
|
|
20110
|
-
|
|
20111
|
-
text: metaText.author,
|
|
20112
|
-
font: 'composerfont',
|
|
20113
|
-
klass: 'meta-top author',
|
|
20625
|
+
var klass = shouldAddClasses ? 'abcjs-author' : '';
|
|
20626
|
+
richText(this.rows, metaText.author, 'composerfont', klass, "author", paddingLeft + width, {
|
|
20114
20627
|
anchor: "end",
|
|
20115
20628
|
absElemType: "author",
|
|
20116
|
-
info: metaTextInfo.author
|
|
20117
|
-
name: "author"
|
|
20629
|
+
info: metaTextInfo.author
|
|
20118
20630
|
}, getTextSize);
|
|
20119
20631
|
}
|
|
20120
20632
|
if (metaText.partOrder && metaText.partOrder.length > 0) {
|
|
20121
|
-
|
|
20122
|
-
|
|
20123
|
-
text: metaText.partOrder,
|
|
20124
|
-
font: 'partsfont',
|
|
20125
|
-
klass: 'meta-top part-order',
|
|
20633
|
+
var klass = shouldAddClasses ? 'abcjs-part-order' : '';
|
|
20634
|
+
richText(this.rows, metaText.partOrder, 'partsfont', klass, "part-order", paddingLeft, {
|
|
20126
20635
|
absElemType: "partOrder",
|
|
20127
20636
|
info: metaTextInfo.partOrder,
|
|
20128
|
-
|
|
20637
|
+
anchor: 'start'
|
|
20129
20638
|
}, getTextSize);
|
|
20130
20639
|
}
|
|
20131
20640
|
}
|
|
@@ -20671,7 +21180,7 @@ var glyphs = {
|
|
|
20671
21180
|
h: 7.515
|
|
20672
21181
|
},
|
|
20673
21182
|
',': {
|
|
20674
|
-
d: [['M', 1.
|
|
21183
|
+
d: [['M', 1.85, -3.36], ['c', 0.57, -0.15, 1.17, 0.03, 1.59, 0.45], ['c', 0.45, 0.45, 0.60, 0.96, 0.51, 1.89], ['c', -0.09, 1.23, -0.42, 2.46, -0.99, 3.93], ['c', -0.30, 0.72, -0.72, 1.62, -0.78, 1.68], ['c', -0.18, 0.21, -0.51, 0.18, -0.66, -0.06], ['c', -0.03, -0.06, -0.06, -0.15, -0.06, -0.18], ['c', 0.00, -0.06, 0.12, -0.33, 0.24, -0.63], ['c', 0.84, -1.80, 1.02, -2.61, 0.69, -3.24], ['c', -0.12, -0.24, -0.27, -0.36, -0.75, -0.60], ['c', -0.36, -0.15, -0.42, -0.21, -0.60, -0.39], ['c', -0.69, -0.69, -0.69, -1.71, 0.00, -2.40], ['c', 0.21, -0.21, 0.51, -0.39, 0.81, -0.45], ['z']],
|
|
20675
21184
|
w: 3.452,
|
|
20676
21185
|
h: 8.143
|
|
20677
21186
|
},
|
|
@@ -20940,7 +21449,10 @@ function drawAbsolute(renderer, params, bartop, selectables, staffPos) {
|
|
|
20940
21449
|
drawTempo(renderer, child);
|
|
20941
21450
|
break;
|
|
20942
21451
|
default:
|
|
20943
|
-
drawRelativeElement(renderer, child, bartop);
|
|
21452
|
+
var el = drawRelativeElement(renderer, child, bartop);
|
|
21453
|
+
if (child.type === "symbol" && child.c && child.c.indexOf('notehead') >= 0) {
|
|
21454
|
+
el.setAttribute('class', 'abcjs-notehead');
|
|
21455
|
+
}
|
|
20944
21456
|
}
|
|
20945
21457
|
}
|
|
20946
21458
|
var klass = params.type;
|
|
@@ -21243,7 +21755,9 @@ var spacing = __webpack_require__(/*! ../helpers/spacing */ "./src/write/helpers
|
|
|
21243
21755
|
var Selectables = __webpack_require__(/*! ./selectables */ "./src/write/draw/selectables.js");
|
|
21244
21756
|
function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, selectTypes, tuneNumber, lineOffset) {
|
|
21245
21757
|
var selectables = new Selectables(renderer.paper, selectTypes, tuneNumber);
|
|
21246
|
-
|
|
21758
|
+
var groupClasses = {};
|
|
21759
|
+
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-meta-top";
|
|
21760
|
+
renderer.paper.openGroup(groupClasses);
|
|
21247
21761
|
renderer.moveY(renderer.padding.top);
|
|
21248
21762
|
nonMusic(renderer, abcTune.topText, selectables);
|
|
21249
21763
|
renderer.paper.closeGroup();
|
|
@@ -21253,7 +21767,8 @@ function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, se
|
|
|
21253
21767
|
classes.incrLine();
|
|
21254
21768
|
var abcLine = abcTune.lines[line];
|
|
21255
21769
|
if (abcLine.staff) {
|
|
21256
|
-
|
|
21770
|
+
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-staff-wrapper abcjs-l" + classes.lineNumber;
|
|
21771
|
+
renderer.paper.openGroup(groupClasses);
|
|
21257
21772
|
if (abcLine.vskip) {
|
|
21258
21773
|
renderer.moveY(abcLine.vskip);
|
|
21259
21774
|
}
|
|
@@ -21263,14 +21778,16 @@ function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, se
|
|
|
21263
21778
|
staffgroups.push(staffgroup);
|
|
21264
21779
|
renderer.paper.closeGroup();
|
|
21265
21780
|
} else if (abcLine.nonMusic) {
|
|
21266
|
-
|
|
21781
|
+
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-non-music";
|
|
21782
|
+
renderer.paper.openGroup(groupClasses);
|
|
21267
21783
|
nonMusic(renderer, abcLine.nonMusic, selectables);
|
|
21268
21784
|
renderer.paper.closeGroup();
|
|
21269
21785
|
}
|
|
21270
21786
|
}
|
|
21271
21787
|
classes.reset();
|
|
21272
21788
|
if (abcTune.bottomText && abcTune.bottomText.rows && abcTune.bottomText.rows.length > 0) {
|
|
21273
|
-
|
|
21789
|
+
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-meta-bottom";
|
|
21790
|
+
renderer.paper.openGroup(groupClasses);
|
|
21274
21791
|
renderer.moveY(24); // TODO-PER: Empirically discovered. What variable should this be?
|
|
21275
21792
|
nonMusic(renderer, abcTune.bottomText, selectables);
|
|
21276
21793
|
renderer.paper.closeGroup();
|
|
@@ -21554,12 +22071,14 @@ function nonMusic(renderer, obj, selectables) {
|
|
|
21554
22071
|
renderer.absolutemoveY(row.absmove);
|
|
21555
22072
|
} else if (row.move) {
|
|
21556
22073
|
renderer.moveY(row.move);
|
|
21557
|
-
} else if (row.text) {
|
|
22074
|
+
} else if (row.text || row.phrases) {
|
|
21558
22075
|
var x = row.left ? row.left : 0;
|
|
21559
22076
|
var el = renderText(renderer, {
|
|
21560
22077
|
x: x,
|
|
21561
22078
|
y: renderer.y,
|
|
21562
22079
|
text: row.text,
|
|
22080
|
+
phrases: row.phrases,
|
|
22081
|
+
'dominant-baseline': row['dominant-baseline'],
|
|
21563
22082
|
type: row.font,
|
|
21564
22083
|
klass: row.klass,
|
|
21565
22084
|
name: row.name,
|
|
@@ -21841,7 +22360,7 @@ function drawRelativeElement(renderer, params, bartop) {
|
|
|
21841
22360
|
case "tabNumber":
|
|
21842
22361
|
var hAnchor = "middle";
|
|
21843
22362
|
var tabFont = "tabnumberfont";
|
|
21844
|
-
var tabClass = 'tab-number';
|
|
22363
|
+
var tabClass = 'abcjs-tab-number';
|
|
21845
22364
|
if (params.isGrace) {
|
|
21846
22365
|
tabFont = "tabgracefont";
|
|
21847
22366
|
y += 2.5;
|
|
@@ -22548,7 +23067,6 @@ function drawTempo(renderer, params) {
|
|
|
22548
23067
|
klass: 'abcjs-tempo',
|
|
22549
23068
|
anchor: "start",
|
|
22550
23069
|
noClass: true,
|
|
22551
|
-
"dominant-baseline": "ideographic",
|
|
22552
23070
|
name: "pre"
|
|
22553
23071
|
}, true);
|
|
22554
23072
|
size = renderer.controller.getTextSize.calc(params.tempo.preString, 'tempofont', 'tempo', text);
|
|
@@ -22608,6 +23126,13 @@ module.exports = drawTempo;
|
|
|
22608
23126
|
var roundNumber = __webpack_require__(/*! ./round-number */ "./src/write/draw/round-number.js");
|
|
22609
23127
|
function renderText(renderer, params, alreadyInGroup) {
|
|
22610
23128
|
var y = params.y;
|
|
23129
|
+
|
|
23130
|
+
// TODO-PER: Probably need to merge the regular text and rich text better. At the least, rich text loses the font box.
|
|
23131
|
+
if (params.phrases) {
|
|
23132
|
+
//richTextLine = function (phrases, x, y, klass, anchor, target)
|
|
23133
|
+
var elem = renderer.paper.richTextLine(params.phrases, params.x, params.y, params.klass, params.anchor);
|
|
23134
|
+
return elem;
|
|
23135
|
+
}
|
|
22611
23136
|
if (params.lane) {
|
|
22612
23137
|
var laneMargin = params.dim.font.size * 0.25;
|
|
22613
23138
|
y += (params.dim.font.size + laneMargin) * params.lane;
|
|
@@ -22618,6 +23143,7 @@ function renderText(renderer, params, alreadyInGroup) {
|
|
|
22618
23143
|
hash.attr["class"] = params.klass;
|
|
22619
23144
|
} else hash = renderer.controller.getFontAndAttr.calc(params.type, params.klass);
|
|
22620
23145
|
if (params.anchor) hash.attr["text-anchor"] = params.anchor;
|
|
23146
|
+
if (params['dominant-baseline']) hash.attr["dominant-baseline"] = params['dominant-baseline'];
|
|
22621
23147
|
hash.attr.x = params.x;
|
|
22622
23148
|
hash.attr.y = y;
|
|
22623
23149
|
if (!params.centerVertically) hash.attr.y += hash.font.size;
|
|
@@ -22991,6 +23517,7 @@ var GetFontAndAttr = __webpack_require__(/*! ./helpers/get-font-and-attr */ "./s
|
|
|
22991
23517
|
var GetTextSize = __webpack_require__(/*! ./helpers/get-text-size */ "./src/write/helpers/get-text-size.js");
|
|
22992
23518
|
var draw = __webpack_require__(/*! ./draw/draw */ "./src/write/draw/draw.js");
|
|
22993
23519
|
var tablatures = __webpack_require__(/*! ../api/abc_tablatures */ "./src/api/abc_tablatures.js");
|
|
23520
|
+
var findSelectableElement = __webpack_require__(/*! ./interactive/find-selectable-element */ "./src/write/interactive/find-selectable-element.js");
|
|
22994
23521
|
|
|
22995
23522
|
/**
|
|
22996
23523
|
* @class
|
|
@@ -23006,6 +23533,7 @@ var tablatures = __webpack_require__(/*! ../api/abc_tablatures */ "./src/api/abc
|
|
|
23006
23533
|
*/
|
|
23007
23534
|
var EngraverController = function EngraverController(paper, params) {
|
|
23008
23535
|
params = params || {};
|
|
23536
|
+
this.findSelectableElement = findSelectableElement;
|
|
23009
23537
|
this.oneSvgPerLine = params.oneSvgPerLine;
|
|
23010
23538
|
this.selectionColor = params.selectionColor;
|
|
23011
23539
|
this.dragColor = params.dragColor ? params.dragColor : params.selectionColor;
|
|
@@ -23014,6 +23542,7 @@ var EngraverController = function EngraverController(paper, params) {
|
|
|
23014
23542
|
this.responsive = params.responsive;
|
|
23015
23543
|
this.space = 3 * spacing.SPACE;
|
|
23016
23544
|
this.initialClef = params.initialClef;
|
|
23545
|
+
this.timeBasedLayout = params.timeBasedLayout;
|
|
23017
23546
|
this.expandToWidest = !!params.expandToWidest;
|
|
23018
23547
|
this.scale = params.scale ? parseFloat(params.scale) : 0;
|
|
23019
23548
|
this.classes = new Classes({
|
|
@@ -23036,6 +23565,7 @@ var EngraverController = function EngraverController(paper, params) {
|
|
|
23036
23565
|
this.renderer.setPaddingOverride(params);
|
|
23037
23566
|
if (params.showDebug) this.renderer.showDebug = params.showDebug;
|
|
23038
23567
|
if (params.jazzchords) this.jazzchords = params.jazzchords;
|
|
23568
|
+
if (params.accentAbove) this.accentAbove = params.accentAbove;
|
|
23039
23569
|
if (params.germanAlphabet) this.germanAlphabet = params.germanAlphabet;
|
|
23040
23570
|
if (params.lineThickness) this.lineThickness = params.lineThickness;
|
|
23041
23571
|
this.renderer.controller = this; // TODO-GD needed for highlighting
|
|
@@ -23087,11 +23617,12 @@ EngraverController.prototype.getMeasureWidths = function (abcTune) {
|
|
|
23087
23617
|
this.reset();
|
|
23088
23618
|
this.getFontAndAttr = new GetFontAndAttr(abcTune.formatting, this.classes);
|
|
23089
23619
|
this.getTextSize = new GetTextSize(this.getFontAndAttr, this.renderer.paper);
|
|
23620
|
+
var origJazzChords = this.jazzchords;
|
|
23090
23621
|
this.setupTune(abcTune, 0);
|
|
23091
23622
|
this.constructTuneElements(abcTune);
|
|
23092
23623
|
// layout() sets the x-coordinate of the abcTune element here:
|
|
23093
23624
|
// abcTune.lines[0].staffGroup.voices[0].children[0].x
|
|
23094
|
-
layout(this.renderer, abcTune, 0, this.space);
|
|
23625
|
+
layout(this.renderer, abcTune, 0, this.space, this.timeBasedLayout);
|
|
23095
23626
|
var ret = [];
|
|
23096
23627
|
var section;
|
|
23097
23628
|
var needNewSection = true;
|
|
@@ -23130,11 +23661,13 @@ EngraverController.prototype.getMeasureWidths = function (abcTune) {
|
|
|
23130
23661
|
//section.height += calcHeight(abcLine.staffGroup) * spacing.STEP;
|
|
23131
23662
|
} else needNewSection = true;
|
|
23132
23663
|
}
|
|
23664
|
+
this.jazzchords = origJazzChords;
|
|
23133
23665
|
return ret;
|
|
23134
23666
|
};
|
|
23135
23667
|
EngraverController.prototype.setupTune = function (abcTune, tuneNumber) {
|
|
23136
23668
|
this.classes.reset();
|
|
23137
23669
|
if (abcTune.formatting.jazzchords !== undefined) this.jazzchords = abcTune.formatting.jazzchords;
|
|
23670
|
+
if (abcTune.formatting.accentAbove !== undefined) this.accentAbove = abcTune.formatting.accentAbove;
|
|
23138
23671
|
this.renderer.newTune(abcTune);
|
|
23139
23672
|
this.engraver = new AbstractEngraver(this.getTextSize, tuneNumber, {
|
|
23140
23673
|
bagpipes: abcTune.formatting.bagpipes,
|
|
@@ -23144,6 +23677,8 @@ EngraverController.prototype.setupTune = function (abcTune, tuneNumber) {
|
|
|
23144
23677
|
percmap: abcTune.formatting.percmap,
|
|
23145
23678
|
initialClef: this.initialClef,
|
|
23146
23679
|
jazzchords: this.jazzchords,
|
|
23680
|
+
timeBasedLayout: this.timeBasedLayout,
|
|
23681
|
+
accentAbove: this.accentAbove,
|
|
23147
23682
|
germanAlphabet: this.germanAlphabet
|
|
23148
23683
|
});
|
|
23149
23684
|
this.engraver.setStemHeight(this.renderer.spacing.stemHeight);
|
|
@@ -23162,7 +23697,7 @@ EngraverController.prototype.setupTune = function (abcTune, tuneNumber) {
|
|
|
23162
23697
|
return scale;
|
|
23163
23698
|
};
|
|
23164
23699
|
EngraverController.prototype.constructTuneElements = function (abcTune) {
|
|
23165
|
-
abcTune.topText = new TopText(abcTune.metaText, abcTune.metaTextInfo, abcTune.formatting, abcTune.lines, this.width, this.renderer.isPrint, this.renderer.padding.left, this.renderer.spacing, this.getTextSize);
|
|
23700
|
+
abcTune.topText = new TopText(abcTune.metaText, abcTune.metaTextInfo, abcTune.formatting, abcTune.lines, this.width, this.renderer.isPrint, this.renderer.padding.left, this.renderer.spacing, this.classes.shouldAddClasses, this.getTextSize);
|
|
23166
23701
|
|
|
23167
23702
|
// Generate the raw staff line data
|
|
23168
23703
|
var i;
|
|
@@ -23189,20 +23724,49 @@ EngraverController.prototype.constructTuneElements = function (abcTune) {
|
|
|
23189
23724
|
abcLine.nonMusic = new Separator(abcLine.separator.spaceAbove, abcLine.separator.lineLength, abcLine.separator.spaceBelow);
|
|
23190
23725
|
}
|
|
23191
23726
|
}
|
|
23192
|
-
abcTune.bottomText = new BottomText(abcTune.metaText, this.width, this.renderer.isPrint, this.renderer.padding.left, this.renderer.spacing, this.getTextSize);
|
|
23727
|
+
abcTune.bottomText = new BottomText(abcTune.metaText, this.width, this.renderer.isPrint, this.renderer.padding.left, this.renderer.spacing, this.classes.shouldAddClasses, this.getTextSize);
|
|
23193
23728
|
};
|
|
23194
23729
|
EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOffset) {
|
|
23730
|
+
var origJazzChords = this.jazzchords;
|
|
23195
23731
|
var scale = this.setupTune(abcTune, tuneNumber);
|
|
23196
23732
|
|
|
23197
23733
|
// Create all of the element objects that will appear on the page.
|
|
23198
23734
|
this.constructTuneElements(abcTune);
|
|
23199
23735
|
|
|
23736
|
+
//Set the top text now that we know the width
|
|
23737
|
+
|
|
23200
23738
|
// Do all the positioning, both horizontally and vertically
|
|
23201
|
-
var maxWidth = layout(this.renderer, abcTune, this.width, this.space, this.expandToWidest);
|
|
23739
|
+
var maxWidth = layout(this.renderer, abcTune, this.width, this.space, this.expandToWidest, this.timeBasedLayout);
|
|
23202
23740
|
|
|
23203
23741
|
//Set the top text now that we know the width
|
|
23204
23742
|
if (this.expandToWidest && maxWidth > this.width + 1) {
|
|
23205
|
-
abcTune.topText = new TopText(abcTune.metaText, abcTune.metaTextInfo, abcTune.formatting, abcTune.lines, maxWidth, this.renderer.isPrint, this.renderer.padding.left, this.renderer.spacing, this.getTextSize);
|
|
23743
|
+
abcTune.topText = new TopText(abcTune.metaText, abcTune.metaTextInfo, abcTune.formatting, abcTune.lines, maxWidth, this.renderer.isPrint, this.renderer.padding.left, this.renderer.spacing, this.classes.shouldAddClasses, this.getTextSize);
|
|
23744
|
+
if (abcTune.lines && abcTune.lines.length > 0) {
|
|
23745
|
+
var nlines = abcTune.lines.length;
|
|
23746
|
+
for (var i = 0; i < nlines; ++i) {
|
|
23747
|
+
var entry = abcTune.lines[i];
|
|
23748
|
+
if (entry.nonMusic) {
|
|
23749
|
+
if (entry.nonMusic.rows && entry.nonMusic.rows.length > 0) {
|
|
23750
|
+
var nRows = entry.nonMusic.rows.length;
|
|
23751
|
+
for (var j = 0; j < nRows; ++j) {
|
|
23752
|
+
var thisRow = entry.nonMusic.rows[j];
|
|
23753
|
+
// Recenter the element if it's a subtitle or centered text
|
|
23754
|
+
if (thisRow.left) {
|
|
23755
|
+
if (entry.subtitle) {
|
|
23756
|
+
thisRow.left = maxWidth / 2 + this.renderer.padding.left;
|
|
23757
|
+
} else {
|
|
23758
|
+
if (entry.text && entry.text.length > 0) {
|
|
23759
|
+
if (entry.text[0].center) {
|
|
23760
|
+
thisRow.left = maxWidth / 2 + this.renderer.padding.left;
|
|
23761
|
+
}
|
|
23762
|
+
}
|
|
23763
|
+
}
|
|
23764
|
+
}
|
|
23765
|
+
}
|
|
23766
|
+
}
|
|
23767
|
+
}
|
|
23768
|
+
}
|
|
23769
|
+
}
|
|
23206
23770
|
}
|
|
23207
23771
|
|
|
23208
23772
|
// Deal with tablature for staff
|
|
@@ -23216,13 +23780,14 @@ EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOf
|
|
|
23216
23780
|
this.selectables = ret.selectables;
|
|
23217
23781
|
if (this.oneSvgPerLine) {
|
|
23218
23782
|
var div = this.renderer.paper.svg.parentNode;
|
|
23219
|
-
this.svgs = splitSvgIntoLines(this.renderer, div, abcTune.metaText.title, this.responsive);
|
|
23783
|
+
this.svgs = splitSvgIntoLines(this.renderer, div, abcTune.metaText.title, this.responsive, scale);
|
|
23220
23784
|
} else {
|
|
23221
23785
|
this.svgs = [this.renderer.paper.svg];
|
|
23222
23786
|
}
|
|
23223
23787
|
setupSelection(this, this.svgs);
|
|
23788
|
+
this.jazzchords = origJazzChords;
|
|
23224
23789
|
};
|
|
23225
|
-
function splitSvgIntoLines(renderer, output, title, responsive) {
|
|
23790
|
+
function splitSvgIntoLines(renderer, output, title, responsive, scale) {
|
|
23226
23791
|
// Each line is a top level <g> in the svg. To split it into separate
|
|
23227
23792
|
// svgs iterate through each of those and put them in a new svg. Since
|
|
23228
23793
|
// they are placed absolutely, the viewBox needs to be manipulated to
|
|
@@ -23245,7 +23810,7 @@ function splitSvgIntoLines(renderer, output, title, responsive) {
|
|
|
23245
23810
|
var height = box.height + gapBetweenLines;
|
|
23246
23811
|
var wrapper = document.createElement("div");
|
|
23247
23812
|
var divStyles = "overflow: hidden;";
|
|
23248
|
-
if (responsive !== 'resize') divStyles += "height:" + height + "px;";
|
|
23813
|
+
if (responsive !== 'resize') divStyles += "height:" + height * scale + "px;";
|
|
23249
23814
|
wrapper.setAttribute("style", divStyles);
|
|
23250
23815
|
var svg = duplicateSvg(source);
|
|
23251
23816
|
var fullTitle = "Sheet Music for \"" + title + "\" section " + (i + 1);
|
|
@@ -23368,7 +23933,7 @@ Classes.prototype.generate = function (c) {
|
|
|
23368
23933
|
if (!this.shouldAddClasses) return "";
|
|
23369
23934
|
var ret = [];
|
|
23370
23935
|
if (c && c.length > 0) ret.push(c);
|
|
23371
|
-
if (c === "tab-number")
|
|
23936
|
+
if (c === "abcjs-tab-number")
|
|
23372
23937
|
// TODO-PER-HACK! straighten out the tablature
|
|
23373
23938
|
return ret.join(' ');
|
|
23374
23939
|
if (c === "text instrument-name") return "abcjs-text abcjs-instrument-name";
|
|
@@ -23409,6 +23974,12 @@ GetFontAndAttr.prototype.updateFonts = function (fontOverrides) {
|
|
|
23409
23974
|
if (fontOverrides.annotationfont) this.formatting.annotationfont = fontOverrides.annotationfont;
|
|
23410
23975
|
if (fontOverrides.vocalfont) this.formatting.vocalfont = fontOverrides.vocalfont;
|
|
23411
23976
|
};
|
|
23977
|
+
GetFontAndAttr.prototype.getFamily = function (type) {
|
|
23978
|
+
if (type[0] === '"' && type[type.length - 1] === '"') {
|
|
23979
|
+
return type.substring(1, type.length - 1);
|
|
23980
|
+
}
|
|
23981
|
+
return type;
|
|
23982
|
+
};
|
|
23412
23983
|
GetFontAndAttr.prototype.calc = function (type, klass) {
|
|
23413
23984
|
var font;
|
|
23414
23985
|
if (typeof type === 'string') {
|
|
@@ -23441,7 +24012,7 @@ GetFontAndAttr.prototype.calc = function (type, klass) {
|
|
|
23441
24012
|
var attr = {
|
|
23442
24013
|
"font-size": font.size,
|
|
23443
24014
|
'font-style': font.style,
|
|
23444
|
-
"font-family": font.face,
|
|
24015
|
+
"font-family": this.getFamily(font.face),
|
|
23445
24016
|
'font-weight': font.weight,
|
|
23446
24017
|
'text-decoration': font.decoration,
|
|
23447
24018
|
'class': this.classes.generate(klass)
|
|
@@ -23471,6 +24042,12 @@ GetTextSize.prototype.updateFonts = function (fontOverrides) {
|
|
|
23471
24042
|
GetTextSize.prototype.attr = function (type, klass) {
|
|
23472
24043
|
return this.getFontAndAttr.calc(type, klass);
|
|
23473
24044
|
};
|
|
24045
|
+
GetTextSize.prototype.getFamily = function (type) {
|
|
24046
|
+
if (type[0] === '"' && type[type.length - 1] === '"') {
|
|
24047
|
+
return type.substring(1, type.length - 1);
|
|
24048
|
+
}
|
|
24049
|
+
return type;
|
|
24050
|
+
};
|
|
23474
24051
|
GetTextSize.prototype.calc = function (text, type, klass, el) {
|
|
23475
24052
|
var hash;
|
|
23476
24053
|
// This can be passed in either a string or a font. If it is a string it names one of the standard fonts.
|
|
@@ -23486,7 +24063,7 @@ GetTextSize.prototype.calc = function (text, type, klass, el) {
|
|
|
23486
24063
|
attr: {
|
|
23487
24064
|
"font-size": type.size,
|
|
23488
24065
|
"font-style": type.style,
|
|
23489
|
-
"font-family": type.face,
|
|
24066
|
+
"font-family": this.getFamily(type.face),
|
|
23490
24067
|
"font-weight": type.weight,
|
|
23491
24068
|
"text-decoration": type.decoration,
|
|
23492
24069
|
"class": this.getFontAndAttr.classes.generate(klass)
|
|
@@ -23560,6 +24137,98 @@ module.exports = spacing;
|
|
|
23560
24137
|
|
|
23561
24138
|
/***/ }),
|
|
23562
24139
|
|
|
24140
|
+
/***/ "./src/write/interactive/create-analysis.js":
|
|
24141
|
+
/*!**************************************************!*\
|
|
24142
|
+
!*** ./src/write/interactive/create-analysis.js ***!
|
|
24143
|
+
\**************************************************/
|
|
24144
|
+
/***/ (function(module) {
|
|
24145
|
+
|
|
24146
|
+
function findNumber(klass, match, target, name) {
|
|
24147
|
+
if (klass.indexOf(match) === 0) {
|
|
24148
|
+
var value = klass.replace(match, '');
|
|
24149
|
+
var num = parseInt(value, 10);
|
|
24150
|
+
if ('' + num === value) target[name] = num;
|
|
24151
|
+
}
|
|
24152
|
+
}
|
|
24153
|
+
function createAnalysis(target, ev) {
|
|
24154
|
+
var classes = [];
|
|
24155
|
+
if (target.absEl.elemset) {
|
|
24156
|
+
var classObj = {};
|
|
24157
|
+
for (var j = 0; j < target.absEl.elemset.length; j++) {
|
|
24158
|
+
var es = target.absEl.elemset[j];
|
|
24159
|
+
if (es) {
|
|
24160
|
+
var klass = es.getAttribute("class").split(' ');
|
|
24161
|
+
for (var k = 0; k < klass.length; k++) {
|
|
24162
|
+
classObj[klass[k]] = true;
|
|
24163
|
+
}
|
|
24164
|
+
}
|
|
24165
|
+
}
|
|
24166
|
+
for (var kk = 0; kk < Object.keys(classObj).length; kk++) {
|
|
24167
|
+
classes.push(Object.keys(classObj)[kk]);
|
|
24168
|
+
}
|
|
24169
|
+
}
|
|
24170
|
+
var analysis = {};
|
|
24171
|
+
for (var ii = 0; ii < classes.length; ii++) {
|
|
24172
|
+
findNumber(classes[ii], "abcjs-v", analysis, "voice");
|
|
24173
|
+
findNumber(classes[ii], "abcjs-l", analysis, "line");
|
|
24174
|
+
findNumber(classes[ii], "abcjs-m", analysis, "measure");
|
|
24175
|
+
}
|
|
24176
|
+
if (target.staffPos) analysis.staffPos = target.staffPos;
|
|
24177
|
+
var closest = ev.target;
|
|
24178
|
+
while (closest && closest.dataset && !closest.dataset.name && closest.tagName.toLowerCase() !== 'svg') {
|
|
24179
|
+
closest = closest.parentNode;
|
|
24180
|
+
}
|
|
24181
|
+
var parent = ev.target;
|
|
24182
|
+
while (parent && parent.dataset && !parent.dataset.index && parent.tagName.toLowerCase() !== 'svg') {
|
|
24183
|
+
parent = parent.parentNode;
|
|
24184
|
+
}
|
|
24185
|
+
if (parent && parent.dataset) {
|
|
24186
|
+
analysis.name = parent.dataset.name;
|
|
24187
|
+
analysis.clickedName = closest.dataset.name;
|
|
24188
|
+
analysis.parentClasses = parent.classList;
|
|
24189
|
+
}
|
|
24190
|
+
if (closest && closest.classList) analysis.clickedClasses = closest.classList;
|
|
24191
|
+
analysis.selectableElement = target.svgEl;
|
|
24192
|
+
return {
|
|
24193
|
+
classes: classes,
|
|
24194
|
+
analysis: analysis
|
|
24195
|
+
};
|
|
24196
|
+
}
|
|
24197
|
+
module.exports = createAnalysis;
|
|
24198
|
+
|
|
24199
|
+
/***/ }),
|
|
24200
|
+
|
|
24201
|
+
/***/ "./src/write/interactive/find-selectable-element.js":
|
|
24202
|
+
/*!**********************************************************!*\
|
|
24203
|
+
!*** ./src/write/interactive/find-selectable-element.js ***!
|
|
24204
|
+
\**********************************************************/
|
|
24205
|
+
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
24206
|
+
|
|
24207
|
+
var createAnalysis = __webpack_require__(/*! ./create-analysis */ "./src/write/interactive/create-analysis.js");
|
|
24208
|
+
function findSelectableElement(event) {
|
|
24209
|
+
var selectable = event;
|
|
24210
|
+
while (selectable && selectable.attributes && selectable.tagName.toLowerCase() !== 'svg' && !selectable.attributes.selectable) {
|
|
24211
|
+
selectable = selectable.parentNode;
|
|
24212
|
+
}
|
|
24213
|
+
if (selectable && selectable.attributes && selectable.attributes.selectable) {
|
|
24214
|
+
var index = selectable.attributes['data-index'].nodeValue;
|
|
24215
|
+
if (index) {
|
|
24216
|
+
index = parseInt(index, 10);
|
|
24217
|
+
if (index >= 0 && index < this.selectables.length) {
|
|
24218
|
+
var element = this.selectables[index];
|
|
24219
|
+
var ret = createAnalysis(element, event);
|
|
24220
|
+
ret.index = index;
|
|
24221
|
+
ret.element = element;
|
|
24222
|
+
return ret;
|
|
24223
|
+
}
|
|
24224
|
+
}
|
|
24225
|
+
}
|
|
24226
|
+
return null;
|
|
24227
|
+
}
|
|
24228
|
+
module.exports = findSelectableElement;
|
|
24229
|
+
|
|
24230
|
+
/***/ }),
|
|
24231
|
+
|
|
23563
24232
|
/***/ "./src/write/interactive/highlight.js":
|
|
23564
24233
|
/*!********************************************!*\
|
|
23565
24234
|
!*** ./src/write/interactive/highlight.js ***!
|
|
@@ -23583,6 +24252,7 @@ module.exports = highlight;
|
|
|
23583
24252
|
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
23584
24253
|
|
|
23585
24254
|
var spacing = __webpack_require__(/*! ../helpers/spacing */ "./src/write/helpers/spacing.js");
|
|
24255
|
+
var createAnalysis = __webpack_require__(/*! ./create-analysis */ "./src/write/interactive/create-analysis.js");
|
|
23586
24256
|
function setupSelection(engraver, svgs) {
|
|
23587
24257
|
engraver.rangeHighlight = rangeHighlight;
|
|
23588
24258
|
if (engraver.dragging) {
|
|
@@ -23888,44 +24558,9 @@ function setSelection(dragIndex) {
|
|
|
23888
24558
|
}
|
|
23889
24559
|
}
|
|
23890
24560
|
function notifySelect(target, dragStep, dragMax, dragIndex, ev) {
|
|
23891
|
-
var
|
|
23892
|
-
|
|
23893
|
-
|
|
23894
|
-
for (var j = 0; j < target.absEl.elemset.length; j++) {
|
|
23895
|
-
var es = target.absEl.elemset[j];
|
|
23896
|
-
if (es) {
|
|
23897
|
-
var klass = es.getAttribute("class").split(' ');
|
|
23898
|
-
for (var k = 0; k < klass.length; k++) {
|
|
23899
|
-
classObj[klass[k]] = true;
|
|
23900
|
-
}
|
|
23901
|
-
}
|
|
23902
|
-
}
|
|
23903
|
-
for (var kk = 0; kk < Object.keys(classObj).length; kk++) {
|
|
23904
|
-
classes.push(Object.keys(classObj)[kk]);
|
|
23905
|
-
}
|
|
23906
|
-
}
|
|
23907
|
-
var analysis = {};
|
|
23908
|
-
for (var ii = 0; ii < classes.length; ii++) {
|
|
23909
|
-
findNumber(classes[ii], "abcjs-v", analysis, "voice");
|
|
23910
|
-
findNumber(classes[ii], "abcjs-l", analysis, "line");
|
|
23911
|
-
findNumber(classes[ii], "abcjs-m", analysis, "measure");
|
|
23912
|
-
}
|
|
23913
|
-
if (target.staffPos) analysis.staffPos = target.staffPos;
|
|
23914
|
-
var closest = ev.target;
|
|
23915
|
-
while (closest && closest.dataset && !closest.dataset.name && closest.tagName.toLowerCase() !== 'svg') {
|
|
23916
|
-
closest = closest.parentNode;
|
|
23917
|
-
}
|
|
23918
|
-
var parent = ev.target;
|
|
23919
|
-
while (parent && parent.dataset && !parent.dataset.index && parent.tagName.toLowerCase() !== 'svg') {
|
|
23920
|
-
parent = parent.parentNode;
|
|
23921
|
-
}
|
|
23922
|
-
if (parent && parent.dataset) {
|
|
23923
|
-
analysis.name = parent.dataset.name;
|
|
23924
|
-
analysis.clickedName = closest.dataset.name;
|
|
23925
|
-
analysis.parentClasses = parent.classList;
|
|
23926
|
-
}
|
|
23927
|
-
if (closest && closest.classList) analysis.clickedClasses = closest.classList;
|
|
23928
|
-
analysis.selectableElement = target.svgEl;
|
|
24561
|
+
var ret = createAnalysis(target, ev);
|
|
24562
|
+
var classes = ret.classes;
|
|
24563
|
+
var analysis = ret.analysis;
|
|
23929
24564
|
for (var i = 0; i < this.listeners.length; i++) {
|
|
23930
24565
|
this.listeners[i](target.absEl.abcelem, target.absEl.tuneNumber, classes.join(' '), analysis, {
|
|
23931
24566
|
step: dragStep,
|
|
@@ -23935,13 +24570,6 @@ function notifySelect(target, dragStep, dragMax, dragIndex, ev) {
|
|
|
23935
24570
|
}, ev);
|
|
23936
24571
|
}
|
|
23937
24572
|
}
|
|
23938
|
-
function findNumber(klass, match, target, name) {
|
|
23939
|
-
if (klass.indexOf(match) === 0) {
|
|
23940
|
-
var value = klass.replace(match, '');
|
|
23941
|
-
var num = parseInt(value, 10);
|
|
23942
|
-
if ('' + num === value) target[name] = num;
|
|
23943
|
-
}
|
|
23944
|
-
}
|
|
23945
24573
|
function clearSelection() {
|
|
23946
24574
|
for (var i = 0; i < this.selected.length; i++) {
|
|
23947
24575
|
this.selected[i].unhighlight(undefined, this.renderer.foregroundColor);
|
|
@@ -24296,6 +24924,94 @@ module.exports = getLeftEdgeOfStaff;
|
|
|
24296
24924
|
|
|
24297
24925
|
/***/ }),
|
|
24298
24926
|
|
|
24927
|
+
/***/ "./src/write/layout/layout-in-grid.js":
|
|
24928
|
+
/*!********************************************!*\
|
|
24929
|
+
!*** ./src/write/layout/layout-in-grid.js ***!
|
|
24930
|
+
\********************************************/
|
|
24931
|
+
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
24932
|
+
|
|
24933
|
+
var getLeftEdgeOfStaff = __webpack_require__(/*! ./get-left-edge-of-staff */ "./src/write/layout/get-left-edge-of-staff.js");
|
|
24934
|
+
function layoutInGrid(renderer, staffGroup, timeBasedLayout) {
|
|
24935
|
+
var leftEdge = getLeftEdgeOfStaff(renderer, staffGroup.getTextSize, staffGroup.voices, staffGroup.brace, staffGroup.bracket);
|
|
24936
|
+
var ret = getTotalDuration(staffGroup, timeBasedLayout.minPadding);
|
|
24937
|
+
var totalDuration = ret.totalDuration;
|
|
24938
|
+
var minSpacing = ret.minSpacing;
|
|
24939
|
+
var totalWidth = minSpacing * totalDuration;
|
|
24940
|
+
if (timeBasedLayout.minWidth) totalWidth = Math.max(totalWidth, timeBasedLayout.minWidth);
|
|
24941
|
+
var leftAlignPadding = timeBasedLayout.minPadding ? timeBasedLayout.minPadding / 2 : 2; // If the padding isn't specified still give it some
|
|
24942
|
+
|
|
24943
|
+
staffGroup.startx = leftEdge;
|
|
24944
|
+
staffGroup.w = totalWidth + leftEdge;
|
|
24945
|
+
for (var i = 0; i < staffGroup.voices.length; i++) {
|
|
24946
|
+
var voice = staffGroup.voices[i];
|
|
24947
|
+
voice.startx = leftEdge;
|
|
24948
|
+
voice.w = totalWidth + leftEdge;
|
|
24949
|
+
var x = leftEdge;
|
|
24950
|
+
var afterFixedLeft = false;
|
|
24951
|
+
var durationUnit = 0;
|
|
24952
|
+
for (var j = 0; j < voice.children.length; j++) {
|
|
24953
|
+
var child = voice.children[j];
|
|
24954
|
+
if (!afterFixedLeft) {
|
|
24955
|
+
if (child.duration !== 0) {
|
|
24956
|
+
// We got to the first music element on the line
|
|
24957
|
+
afterFixedLeft = true;
|
|
24958
|
+
durationUnit = (totalWidth + leftEdge - x) / totalDuration;
|
|
24959
|
+
staffGroup.gridStart = x;
|
|
24960
|
+
} else {
|
|
24961
|
+
// We are still doing the preliminary stuff - clef, time sig, etc.
|
|
24962
|
+
child.x = x;
|
|
24963
|
+
x += child.w + child.minspacing;
|
|
24964
|
+
}
|
|
24965
|
+
}
|
|
24966
|
+
if (afterFixedLeft) {
|
|
24967
|
+
if (timeBasedLayout.align === 'center') child.x = x + child.duration * durationUnit / 2 - child.w / 2;else {
|
|
24968
|
+
// left align with padding - but no padding for barlines, they should be right aligned.
|
|
24969
|
+
// TODO-PER: it looks better to move bar lines one pixel to right. Not sure why.
|
|
24970
|
+
if (child.duration === 0) {
|
|
24971
|
+
child.x = x + 1 - child.w;
|
|
24972
|
+
} else {
|
|
24973
|
+
// child.extraw has the width of the accidentals - push the note to the right to take that into consideration. It will be 0 if there is nothing to the left.
|
|
24974
|
+
child.x = x + leftAlignPadding - child.extraw;
|
|
24975
|
+
}
|
|
24976
|
+
}
|
|
24977
|
+
x += child.duration * durationUnit;
|
|
24978
|
+
}
|
|
24979
|
+
for (var k = 0; k < child.children.length; k++) {
|
|
24980
|
+
var grandchild = child.children[k];
|
|
24981
|
+
// some elements don't have a dx - Tempo, for instance
|
|
24982
|
+
var dx = grandchild.dx ? grandchild.dx : 0;
|
|
24983
|
+
grandchild.x = child.x + dx;
|
|
24984
|
+
}
|
|
24985
|
+
}
|
|
24986
|
+
staffGroup.gridEnd = x;
|
|
24987
|
+
}
|
|
24988
|
+
return totalWidth;
|
|
24989
|
+
}
|
|
24990
|
+
function getTotalDuration(staffGroup, timeBasedLayout) {
|
|
24991
|
+
var maxSpacing = 0;
|
|
24992
|
+
var maxCount = 0;
|
|
24993
|
+
for (var i = 0; i < staffGroup.voices.length; i++) {
|
|
24994
|
+
var count = 0;
|
|
24995
|
+
var voice = staffGroup.voices[i];
|
|
24996
|
+
for (var j = 0; j < voice.children.length; j++) {
|
|
24997
|
+
var element = voice.children[j];
|
|
24998
|
+
count += element.duration;
|
|
24999
|
+
if (element.duration) {
|
|
25000
|
+
var width = (element.w + timeBasedLayout) / element.duration;
|
|
25001
|
+
maxSpacing = Math.max(maxSpacing, width);
|
|
25002
|
+
}
|
|
25003
|
+
}
|
|
25004
|
+
maxCount = Math.max(maxCount, count);
|
|
25005
|
+
}
|
|
25006
|
+
return {
|
|
25007
|
+
totalDuration: maxCount,
|
|
25008
|
+
minSpacing: maxSpacing
|
|
25009
|
+
};
|
|
25010
|
+
}
|
|
25011
|
+
module.exports = layoutInGrid;
|
|
25012
|
+
|
|
25013
|
+
/***/ }),
|
|
25014
|
+
|
|
24299
25015
|
/***/ "./src/write/layout/layout.js":
|
|
24300
25016
|
/*!************************************!*\
|
|
24301
25017
|
!*** ./src/write/layout/layout.js ***!
|
|
@@ -24306,7 +25022,12 @@ var layoutVoice = __webpack_require__(/*! ./voice */ "./src/write/layout/voice.j
|
|
|
24306
25022
|
var setUpperAndLowerElements = __webpack_require__(/*! ./set-upper-and-lower-elements */ "./src/write/layout/set-upper-and-lower-elements.js");
|
|
24307
25023
|
var layoutStaffGroup = __webpack_require__(/*! ./staff-group */ "./src/write/layout/staff-group.js");
|
|
24308
25024
|
var getLeftEdgeOfStaff = __webpack_require__(/*! ./get-left-edge-of-staff */ "./src/write/layout/get-left-edge-of-staff.js");
|
|
24309
|
-
var
|
|
25025
|
+
var layoutInGrid = __webpack_require__(/*! ./layout-in-grid */ "./src/write/layout/layout-in-grid.js");
|
|
25026
|
+
|
|
25027
|
+
// This sets the "x" attribute on all the children in abctune.lines
|
|
25028
|
+
// It also sets the "w" and "startx" attributes on "voices"
|
|
25029
|
+
// It also sets the "w" and "startx" attributes on "voices.children"
|
|
25030
|
+
var layout = function layout(renderer, abctune, width, space, expandToWidest, timeBasedLayout) {
|
|
24310
25031
|
var i;
|
|
24311
25032
|
var abcLine;
|
|
24312
25033
|
// Adjust the x-coordinates to their absolute positions
|
|
@@ -24315,7 +25036,8 @@ var layout = function layout(renderer, abctune, width, space, expandToWidest) {
|
|
|
24315
25036
|
abcLine = abctune.lines[i];
|
|
24316
25037
|
if (abcLine.staff) {
|
|
24317
25038
|
// console.log("=== line", i)
|
|
24318
|
-
var thisWidth
|
|
25039
|
+
var thisWidth;
|
|
25040
|
+
if (timeBasedLayout !== undefined) thisWidth = layoutInGrid(renderer, abcLine.staffGroup, timeBasedLayout);else thisWidth = setXSpacing(renderer, maxWidth, space, abcLine.staffGroup, abctune.formatting, i === abctune.lines.length - 1, false);
|
|
24319
25041
|
// console.log(thisWidth, maxWidth)
|
|
24320
25042
|
if (Math.round(thisWidth) > Math.round(maxWidth)) {
|
|
24321
25043
|
// to take care of floating point weirdness
|
|
@@ -24350,40 +25072,34 @@ var layout = function layout(renderer, abctune, width, space, expandToWidest) {
|
|
|
24350
25072
|
var setXSpacing = function setXSpacing(renderer, width, space, staffGroup, formatting, isLastLine, debug) {
|
|
24351
25073
|
var leftEdge = getLeftEdgeOfStaff(renderer, staffGroup.getTextSize, staffGroup.voices, staffGroup.brace, staffGroup.bracket);
|
|
24352
25074
|
var newspace = space;
|
|
25075
|
+
//dumpGroup("before", staffGroup)
|
|
24353
25076
|
for (var it = 0; it < 8; it++) {
|
|
24354
25077
|
// TODO-PER: shouldn't need multiple passes, but each pass gets it closer to the right spacing. (Only affects long lines: normal lines break out of this loop quickly.)
|
|
24355
25078
|
// console.log("iteration", it)
|
|
24356
|
-
|
|
24357
|
-
var ret = layoutStaffGroup(newspace, renderer, debug, staffGroup, leftEdge);
|
|
24358
|
-
// dumpGroup("after",staffGroup)
|
|
25079
|
+
var ret = layoutStaffGroup(newspace, renderer.minPadding, debug, staffGroup, leftEdge);
|
|
24359
25080
|
newspace = calcHorizontalSpacing(isLastLine, formatting.stretchlast, width + renderer.padding.left, staffGroup.w, newspace, ret.spacingUnits, ret.minSpace, renderer.padding.left + renderer.padding.right);
|
|
24360
25081
|
if (debug) console.log("setXSpace", it, staffGroup.w, newspace, staffGroup.minspace);
|
|
24361
25082
|
if (newspace === null) break;
|
|
24362
25083
|
}
|
|
25084
|
+
//dumpGroup("after",staffGroup)
|
|
24363
25085
|
centerWholeRests(staffGroup.voices);
|
|
24364
25086
|
return staffGroup.w - leftEdge;
|
|
24365
25087
|
};
|
|
24366
|
-
|
|
24367
|
-
//
|
|
24368
|
-
|
|
24369
|
-
|
|
24370
|
-
|
|
24371
|
-
|
|
24372
|
-
|
|
24373
|
-
|
|
24374
|
-
|
|
24375
|
-
|
|
24376
|
-
|
|
24377
|
-
|
|
24378
|
-
|
|
24379
|
-
|
|
24380
|
-
|
|
24381
|
-
// var child = staffGroup.voices[0].children[i]
|
|
24382
|
-
// output.voice.children.push({ fixedW: child.fixed.w, w: child.w, x: child.x, type: child.type })
|
|
24383
|
-
// }
|
|
24384
|
-
// console.log(label,output)
|
|
24385
|
-
// }
|
|
24386
|
-
|
|
25088
|
+
function replacer(key, value) {
|
|
25089
|
+
// Filtering out properties
|
|
25090
|
+
if (key === 'parent') {
|
|
25091
|
+
return 'parent';
|
|
25092
|
+
}
|
|
25093
|
+
if (key === 'beam') {
|
|
25094
|
+
return 'beam';
|
|
25095
|
+
}
|
|
25096
|
+
return value;
|
|
25097
|
+
}
|
|
25098
|
+
function dumpGroup(label, staffGroup) {
|
|
25099
|
+
console.log("=================== " + label + " =========================");
|
|
25100
|
+
console.log(staffGroup);
|
|
25101
|
+
console.log(JSON.stringify(staffGroup, replacer, "\t"));
|
|
25102
|
+
}
|
|
24387
25103
|
function calcHorizontalSpacing(isLastLine, stretchLast, targetWidth, lineWidth, spacing, spacingUnits, minSpace, padding) {
|
|
24388
25104
|
if (isLastLine) {
|
|
24389
25105
|
if (stretchLast === undefined) {
|
|
@@ -24514,6 +25230,7 @@ var setUpperAndLowerElements = function setUpperAndLowerElements(renderer, staff
|
|
|
24514
25230
|
var addedSpace = minSpacingInPitches - forcedSpacingBetween;
|
|
24515
25231
|
if (addedSpace > 0) staff.top += addedSpace;
|
|
24516
25232
|
}
|
|
25233
|
+
staff.top += renderer.spacing.staffTopMargin / spacing.STEP;
|
|
24517
25234
|
lastStaffBottom = 2 - staff.bottom; // the staff starts at position 2 and the bottom variable is negative. Therefore to find out how large the bottom is, we reverse the sign of the bottom, and add the 2 in.
|
|
24518
25235
|
|
|
24519
25236
|
// Now we need a little margin on the top, so we'll just throw that in.
|
|
@@ -24672,7 +25389,7 @@ function checkLastBarX(voices) {
|
|
|
24672
25389
|
}
|
|
24673
25390
|
}
|
|
24674
25391
|
}
|
|
24675
|
-
var layoutStaffGroup = function layoutStaffGroup(spacing,
|
|
25392
|
+
var layoutStaffGroup = function layoutStaffGroup(spacing, minPadding, debug, staffGroup, leftEdge) {
|
|
24676
25393
|
var epsilon = 0.0000001; // Fudging for inexactness of floating point math.
|
|
24677
25394
|
var spacingunits = 0; // number of times we will have ended up using the spacing distance (as opposed to fixed width distances)
|
|
24678
25395
|
var minspace = 1000; // a big number to start off with - used to find out what the smallest space between two notes is -- GD 2014.1.7
|
|
@@ -24728,7 +25445,7 @@ var layoutStaffGroup = function layoutStaffGroup(spacing, renderer, debug, staff
|
|
|
24728
25445
|
if (v.voicenumber === 0) lastTopVoice = i;
|
|
24729
25446
|
var topVoice = lastTopVoice !== undefined && currentvoices[lastTopVoice].voicenumber !== v.voicenumber ? currentvoices[lastTopVoice] : undefined;
|
|
24730
25447
|
if (!isSameStaff(v, topVoice)) topVoice = undefined;
|
|
24731
|
-
var voicechildx = layoutVoiceElements.layoutOneItem(x, spacing, v,
|
|
25448
|
+
var voicechildx = layoutVoiceElements.layoutOneItem(x, spacing, v, minPadding, topVoice);
|
|
24732
25449
|
var dx = voicechildx - x;
|
|
24733
25450
|
if (dx > 0) {
|
|
24734
25451
|
x = voicechildx; //update x
|
|
@@ -24954,7 +25671,7 @@ VoiceElement.shiftRight = function (dx, voice) {
|
|
|
24954
25671
|
|
|
24955
25672
|
// call when spacingduration has been updated
|
|
24956
25673
|
VoiceElement.updateNextX = function (x, spacing, voice) {
|
|
24957
|
-
voice.nextx = x + spacing *
|
|
25674
|
+
voice.nextx = x + spacing * this.getSpacingUnits(voice);
|
|
24958
25675
|
};
|
|
24959
25676
|
VoiceElement.updateIndices = function (voice) {
|
|
24960
25677
|
if (!this.layoutEnded(voice)) {
|
|
@@ -25019,7 +25736,7 @@ function moveDecorations(beam) {
|
|
|
25019
25736
|
var top = yAtNote(child, beam);
|
|
25020
25737
|
for (var i = 0; i < child.children.length; i++) {
|
|
25021
25738
|
var el = child.children[i];
|
|
25022
|
-
if (el.klass === 'ornament') {
|
|
25739
|
+
if (el.klass === 'ornament' && el.position !== 'below') {
|
|
25023
25740
|
if (el.bottom - padding < top) {
|
|
25024
25741
|
var distance = top - el.bottom + padding; // Find the distance that it needs to move and add a little margin so the element doesn't touch the beam.
|
|
25025
25742
|
el.bottom += distance;
|
|
@@ -25223,6 +25940,7 @@ Renderer.prototype.initVerticalSpace = function () {
|
|
|
25223
25940
|
// Set the slur height factor.
|
|
25224
25941
|
staffSeparation: 61.33,
|
|
25225
25942
|
// Do not put a staff system closer than <unit> from the previous system.
|
|
25943
|
+
staffTopMargin: 0,
|
|
25226
25944
|
stemHeight: 26.67 + 10,
|
|
25227
25945
|
// Set the stem height.
|
|
25228
25946
|
subtitle: 3.78,
|
|
@@ -25274,6 +25992,7 @@ Renderer.prototype.setVerticalSpace = function (formatting) {
|
|
|
25274
25992
|
if (formatting.musicspace !== undefined) this.spacing.music = formatting.musicspace * 4 / 3;
|
|
25275
25993
|
if (formatting.titlespace !== undefined) this.spacing.title = formatting.titlespace * 4 / 3;
|
|
25276
25994
|
if (formatting.sysstaffsep !== undefined) this.spacing.systemStaffSeparation = formatting.sysstaffsep * 4 / 3;
|
|
25995
|
+
if (formatting.stafftopmargin !== undefined) this.spacing.staffTopMargin = formatting.stafftopmargin * 4 / 3;
|
|
25277
25996
|
if (formatting.subtitlespace !== undefined) this.spacing.subtitle = formatting.subtitlespace * 4 / 3;
|
|
25278
25997
|
if (formatting.topspace !== undefined) this.spacing.top = formatting.topspace * 4 / 3;
|
|
25279
25998
|
if (formatting.vocalspace !== undefined) this.spacing.vocal = formatting.vocalspace * 4 / 3;
|
|
@@ -25484,6 +26203,28 @@ Svg.prototype.text = function (text, attr, target) {
|
|
|
25484
26203
|
if (target) target.appendChild(el);else this.append(el);
|
|
25485
26204
|
return el;
|
|
25486
26205
|
};
|
|
26206
|
+
Svg.prototype.richTextLine = function (phrases, x, y, klass, anchor, target) {
|
|
26207
|
+
var el = document.createElementNS(svgNS, 'text');
|
|
26208
|
+
el.setAttribute("stroke", "none");
|
|
26209
|
+
el.setAttribute("class", klass);
|
|
26210
|
+
el.setAttribute("x", x);
|
|
26211
|
+
el.setAttribute("y", y);
|
|
26212
|
+
el.setAttribute("text-anchor", anchor);
|
|
26213
|
+
el.setAttribute("dominant-baseline", "middle");
|
|
26214
|
+
for (var i = 0; i < phrases.length; i++) {
|
|
26215
|
+
var phrase = phrases[i];
|
|
26216
|
+
var tspan = document.createElementNS(svgNS, 'tspan');
|
|
26217
|
+
var attrs = Object.keys(phrase.attrs);
|
|
26218
|
+
for (var j = 0; j < attrs.length; j++) {
|
|
26219
|
+
var value = phrase.attrs[attrs[j]];
|
|
26220
|
+
if (value !== '') tspan.setAttribute(attrs[j], value);
|
|
26221
|
+
}
|
|
26222
|
+
tspan.textContent = phrase.content;
|
|
26223
|
+
el.appendChild(tspan);
|
|
26224
|
+
}
|
|
26225
|
+
if (target) target.appendChild(el);else this.append(el);
|
|
26226
|
+
return el;
|
|
26227
|
+
};
|
|
25487
26228
|
Svg.prototype.guessWidth = function (text, attr) {
|
|
25488
26229
|
var svg = this.createDummySvg();
|
|
25489
26230
|
var el = this.text(text, attr, svg);
|
|
@@ -25635,7 +26376,7 @@ module.exports = Svg;
|
|
|
25635
26376
|
\********************/
|
|
25636
26377
|
/***/ (function(module) {
|
|
25637
26378
|
|
|
25638
|
-
var version = '6.
|
|
26379
|
+
var version = '6.4.0';
|
|
25639
26380
|
module.exports = version;
|
|
25640
26381
|
|
|
25641
26382
|
/***/ })
|