abcjs 6.2.3 → 6.3.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.
Files changed (46) hide show
  1. package/README.md +4 -0
  2. package/RELEASE.md +40 -1
  3. package/dist/abcjs-basic-min.js +2 -2
  4. package/dist/abcjs-basic.js +751 -416
  5. package/dist/abcjs-basic.js.map +1 -1
  6. package/dist/abcjs-plugin-min.js +2 -2
  7. package/index.js +2 -0
  8. package/package.json +1 -1
  9. package/plugin.js +1 -1
  10. package/src/api/abc_tablatures.js +48 -13
  11. package/src/parse/abc_parse_directive.js +17 -16
  12. package/src/parse/abc_parse_header.js +22 -19
  13. package/src/parse/abc_tokenizer.js +72 -7
  14. package/src/parse/tune-builder.js +60 -1
  15. package/src/synth/synth-controller.js +6 -2
  16. package/src/tablatures/instruments/string-patterns.js +11 -0
  17. package/src/tablatures/instruments/{guitar/guitar-patterns.js → tab-string-patterns.js} +6 -6
  18. package/src/tablatures/instruments/{violin/tab-violin.js → tab-string.js} +13 -11
  19. package/src/tablatures/tab-absolute-elements.js +16 -7
  20. package/src/tablatures/tab-renderer.js +22 -9
  21. package/src/test/abc_parser_lint.js +13 -12
  22. package/src/write/creation/abstract-engraver.js +3 -2
  23. package/src/write/creation/add-chord.js +102 -82
  24. package/src/write/creation/add-text-if.js +2 -2
  25. package/src/write/creation/decoration.js +14 -8
  26. package/src/write/creation/elements/bottom-text.js +62 -47
  27. package/src/write/creation/elements/rich-text.js +51 -0
  28. package/src/write/creation/elements/top-text.js +37 -11
  29. package/src/write/creation/glyphs.js +1 -1
  30. package/src/write/draw/absolute.js +4 -1
  31. package/src/write/draw/draw.js +13 -4
  32. package/src/write/draw/non-music.js +3 -1
  33. package/src/write/draw/relative.js +1 -1
  34. package/src/write/draw/tempo.js +1 -1
  35. package/src/write/draw/text.js +10 -0
  36. package/src/write/engraver-controller.js +54 -13
  37. package/src/write/helpers/classes.js +1 -1
  38. package/src/write/helpers/get-font-and-attr.js +8 -1
  39. package/src/write/helpers/get-text-size.js +8 -1
  40. package/src/write/svg.js +30 -0
  41. package/temp.txt +50 -0
  42. package/types/index.d.ts +46 -6
  43. package/version.js +1 -1
  44. package/.github/workflows/tests.yml +0 -29
  45. package/src/tablatures/instruments/guitar/tab-guitar.js +0 -48
  46. package/src/tablatures/instruments/violin/violin-patterns.js +0 -23
package/index.js CHANGED
@@ -57,6 +57,7 @@ var supportsAudio = require('./src/synth/supports-audio');
57
57
  var playEvent = require('./src/synth/play-event');
58
58
  var SynthController = require('./src/synth/synth-controller');
59
59
  var getMidiFile = require('./src/synth/get-midi-file');
60
+ var midiRenderer = require('./src/synth/abc_midi_renderer');
60
61
 
61
62
  abcjs.synth = {
62
63
  CreateSynth: CreateSynth,
@@ -71,6 +72,7 @@ abcjs.synth = {
71
72
  playEvent: playEvent,
72
73
  getMidiFile: getMidiFile,
73
74
  sequence: sequence,
75
+ midiRenderer: midiRenderer,
74
76
  };
75
77
 
76
78
  abcjs['Editor'] = require('./src/edit/abc_editor');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abcjs",
3
- "version": "6.2.3",
3
+ "version": "6.3.0",
4
4
  "description": "Renderer for abc music notation",
5
5
  "main": "index.js",
6
6
  "types": "types/index.d.ts",
package/plugin.js CHANGED
@@ -248,7 +248,7 @@ if (autostart &&
248
248
  }
249
249
 
250
250
  var abcjs = {
251
- plugin: Plugin
251
+ plugin: plugin
252
252
  };
253
253
 
254
254
  module.exports = abcjs;
@@ -5,17 +5,17 @@
5
5
  * where plugin represents a plugin instance
6
6
  *
7
7
  */
8
- var ViolinTablature = require('../tablatures/instruments/violin/tab-violin');
9
- var GuitarTablature = require('../tablatures/instruments/guitar/tab-guitar');
8
+ var StringTablature = require('../tablatures/instruments/tab-string');
10
9
 
11
10
  /* extend the table below when adding a new instrument plugin */
12
11
 
13
12
  // Existing tab classes
14
13
  var pluginTab = {
15
- 'violin': 'ViolinTab',
16
- 'fiddle': 'ViolinTab',
17
- 'mandolin': 'ViolinTab',
18
- 'guitar': 'GuitarTab'
14
+ 'violin': { name: 'StringTab', defaultTuning: ['G,', 'D', 'A', 'e'], isTabBig: false, tabSymbolOffset: 0},
15
+ 'fiddle': { name: 'StringTab', defaultTuning: ['G,', 'D', 'A', 'e'], isTabBig: false, tabSymbolOffset: 0},
16
+ 'mandolin': { name: 'StringTab', defaultTuning: ['G,', 'D', 'A', 'e'], isTabBig: false, tabSymbolOffset: 0},
17
+ 'guitar': { name: 'StringTab', defaultTuning: ['E,', 'A,', 'D', 'G' , 'B' , 'e'], isTabBig: true, tabSymbolOffset: 0},
18
+ 'fiveString': { name: 'StringTab', defaultTuning: ['C,', 'G,', 'D', 'A', 'e'], isTabBig: false, tabSymbolOffset: -.95},
19
19
  };
20
20
 
21
21
  var abcTablatures = {
@@ -66,7 +66,7 @@ var abcTablatures = {
66
66
  var tabName = pluginTab[instrument];
67
67
  var plugin = null;
68
68
  if (tabName) {
69
- plugin = this.plugins[tabName];
69
+ plugin = this.plugins[tabName.name];
70
70
  }
71
71
  if (plugin) {
72
72
  if (params.visualTranspose != 0) {
@@ -79,6 +79,7 @@ var abcTablatures = {
79
79
  tuneNumber: tuneNumber,
80
80
  params: args,
81
81
  instance: null,
82
+ tabType: tabName,
82
83
  };
83
84
  // proceed with tab plugin init
84
85
  // plugin.init(tune, tuneNumber, args, ii);
@@ -103,16 +104,50 @@ var abcTablatures = {
103
104
  * @param {*} renderer
104
105
  * @param {*} abcTune
105
106
  */
106
- layoutTablatures: function (renderer, abcTune) {
107
+ layoutTablatures: function layoutTablatures(renderer, abcTune) {
107
108
  var tabs = abcTune.tablatures;
109
+
108
110
  // chack tabs request for each staffs
111
+ var staffLineCount = 0;
112
+
113
+ // Clear the suppression flag
114
+ if (tabs && (tabs.length > 0)){
115
+ var nTabs = tabs.length;
116
+ for (var kk=0;kk<nTabs;++kk){
117
+ if (tabs[kk] && tabs[kk].params.firstStaffOnly){
118
+ tabs[kk].params.suppress = false;
119
+ }
120
+ }
121
+ }
122
+
109
123
  for (var ii = 0; ii < abcTune.lines.length; ii++) {
110
124
  var line = abcTune.lines[ii];
125
+
126
+ if (line.staff){
127
+ staffLineCount++;
128
+ }
129
+
130
+ // MAE 27Nov2023
131
+ // If tab param "firstStaffOnly", remove the tab label after the first staff
132
+ if (staffLineCount > 1){
133
+ if (tabs && (tabs.length > 0)){
134
+ var nTabs = tabs.length;
135
+ for (var kk=0;kk<nTabs;++kk){
136
+ if (tabs[kk].params.firstStaffOnly){
137
+ // Set the staff draw suppression flag
138
+ tabs[kk].params.suppress = true;
139
+ }
140
+ }
141
+ }
142
+ }
143
+
111
144
  var curStaff = line.staff;
112
145
  if (curStaff) {
146
+ var maxStaves = curStaff.length
113
147
  for (var jj = 0; jj < curStaff.length; jj++) {
114
- if (tabs[jj]) {
115
- // tablature requested for staff
148
+
149
+ if (tabs[jj] && jj < maxStaves) {
150
+ // tablature requested for staff
116
151
  var tabPlugin = tabs[jj];
117
152
  if (tabPlugin.instance == null) {
118
153
  tabPlugin.instance = new tabPlugin.classz();
@@ -121,7 +156,8 @@ var abcTablatures = {
121
156
  tabPlugin.instance.init(abcTune,
122
157
  tabPlugin.tuneNumber,
123
158
  tabPlugin.params,
124
- jj
159
+ jj,
160
+ tabPlugin.tabType
125
161
  );
126
162
  }
127
163
  // render next
@@ -138,8 +174,7 @@ var abcTablatures = {
138
174
  init: function () {
139
175
  // just register plugin hosted by abcjs
140
176
  if (!this.inited) {
141
- this.register(new ViolinTablature());
142
- this.register(new GuitarTablature());
177
+ this.register(new StringTablature());
143
178
  this.inited = true;
144
179
  }
145
180
  }
@@ -684,27 +684,27 @@ var parseDirective = {};
684
684
  };
685
685
 
686
686
  parseDirective.parseFontChangeLine = function(textstr) {
687
+ // We don't want to match two dollar signs, so change those temporarily
688
+ textstr = textstr.replace(/\$\$/g,"\x03")
687
689
  var textParts = textstr.split('$');
688
690
  if (textParts.length > 1 && multilineVars.setfont) {
689
- var textarr = [ { text: textParts[0] }];
691
+ var textarr = [ ];
692
+ if (textParts[0] !== '') // did the original string start with `$`?
693
+ textarr.push({ text: textParts[0] })
690
694
  for (var i = 1; i < textParts.length; i++) {
691
695
  if (textParts[i][0] === '0')
692
- textarr.push({ text: textParts[i].substring(1) });
693
- else if (textParts[i][0] === '1' && multilineVars.setfont[1])
694
- textarr.push({font: multilineVars.setfont[1], text: textParts[i].substring(1) });
695
- else if (textParts[i][0] === '2' && multilineVars.setfont[2])
696
- textarr.push({font: multilineVars.setfont[2], text: textParts[i].substring(1) });
697
- else if (textParts[i][0] === '3' && multilineVars.setfont[3])
698
- textarr.push({font: multilineVars.setfont[3], text: textParts[i].substring(1) });
699
- else if (textParts[i][0] === '4' && multilineVars.setfont[4])
700
- textarr.push({font: multilineVars.setfont[4], text: textParts[i].substring(1) });
701
- else
702
- textarr[textarr.length-1].text += '$' + textParts[i];
696
+ textarr.push({ text: textParts[i].substring(1).replace(/\x03/g,"$$") });
697
+ else {
698
+ var whichFont = parseInt(textParts[i][0],10)
699
+ if (multilineVars.setfont[whichFont])
700
+ textarr.push({font: multilineVars.setfont[whichFont], text: textParts[i].substring(1).replace(/\x03/g,"$$") });
701
+ else
702
+ textarr[textarr.length-1].text += '$' + textParts[i].replace(/\x03/g,"$$");
703
+ }
703
704
  }
704
- if (textarr.length > 1)
705
- return textarr;
705
+ return textarr;
706
706
  }
707
- return textstr;
707
+ return textstr.replace(/\x03/g,"$$");
708
708
  };
709
709
 
710
710
  var positionChoices = [ 'auto', 'above', 'below', 'hidden' ];
@@ -748,6 +748,7 @@ var parseDirective = {};
748
748
  case "bagpipes":tune.formatting.bagpipes = true;break;
749
749
  case "flatbeams":tune.formatting.flatbeams = true;break;
750
750
  case "jazzchords":tune.formatting.jazzchords = true;break;
751
+ case "accentAbove":tune.formatting.accentAbove = true;break;
751
752
  case "germanAlphabet":tune.formatting.germanAlphabet = true;break;
752
753
  case "landscape":multilineVars.landscape = true;break;
753
754
  case "papersize":multilineVars.papersize = restOfString;break;
@@ -939,7 +940,7 @@ var parseDirective = {};
939
940
  if (sfTokens.length >= 4) {
940
941
  if (sfTokens[0].token === '-' && sfTokens[1].type === 'number') {
941
942
  var sfNum = parseInt(sfTokens[1].token);
942
- if (sfNum >= 1 && sfNum <= 4) {
943
+ if (sfNum >= 1 && sfNum <= 9) {
943
944
  if (!multilineVars.setfont)
944
945
  multilineVars.setfont = [];
945
946
  sfTokens.shift();
@@ -11,15 +11,12 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
11
11
  };
12
12
  this.reset(tokenizer, warn, multilineVars, tune);
13
13
 
14
- this.setTitle = function(title) {
14
+ this.setTitle = function(title, origSize) {
15
15
  if (multilineVars.hasMainTitle)
16
- tuneBuilder.addSubtitle(tokenizer.translateString(tokenizer.stripComment(title)), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+title.length+2}); // display secondary title
16
+ tuneBuilder.addSubtitle(title, { startChar: multilineVars.iChar, endChar: multilineVars.iChar+origSize+2}); // display secondary title
17
17
  else
18
18
  {
19
- var titleStr = tokenizer.translateString(tokenizer.theReverser(tokenizer.stripComment(title)));
20
- if (multilineVars.titlecaps)
21
- titleStr = titleStr.toUpperCase();
22
- tuneBuilder.addMetaText("title", titleStr, { startChar: multilineVars.iChar, endChar: multilineVars.iChar+title.length+2});
19
+ tuneBuilder.addMetaText("title", title, { startChar: multilineVars.iChar, endChar: multilineVars.iChar+origSize+2});
23
20
  multilineVars.hasMainTitle = true;
24
21
  }
25
22
  };
@@ -371,10 +368,11 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
371
368
  tuneBuilder.appendStartingElement('key', startChar, endChar, parseKeyVoice.fixKey(multilineVars.clef, multilineVars.key));
372
369
  return [ e-i+1+ws ];
373
370
  case "[P:":
371
+ var part = parseDirective.parseFontChangeLine(line.substring(i+3, e))
374
372
  if (startLine || tune.lines.length <= tune.lineNum)
375
- multilineVars.partForNextLine = { title: line.substring(i+3, e), startChar: startChar, endChar: endChar };
373
+ multilineVars.partForNextLine = { title: part, startChar: startChar, endChar: endChar };
376
374
  else
377
- tuneBuilder.appendElement('part', startChar, endChar, {title: line.substring(i+3, e)});
375
+ tuneBuilder.appendElement('part', startChar, endChar, {title: part});
378
376
  return [ e-i+1+ws ];
379
377
  case "[L:":
380
378
  this.setDefaultLength(line, i+3, e);
@@ -477,23 +475,26 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
477
475
 
478
476
  this.parseHeader = function(line) {
479
477
  var field = metaTextHeaders[line[0]];
480
- if (field !== undefined) {
481
- if (field === 'unalignedWords')
482
- tuneBuilder.addMetaTextArray(field, parseDirective.parseFontChangeLine(tokenizer.translateString(tokenizer.stripComment(line.substring(2)))), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
483
- else
484
- tuneBuilder.addMetaText(field, tokenizer.translateString(tokenizer.stripComment(line.substring(2))), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
485
- return {};
478
+ var origSize = line.length-2
479
+ var restOfLine = tokenizer.translateString(tokenizer.stripComment(line.substring(2)))
480
+ if (field === 'unalignedWords' || field === 'notes') {
481
+ // These fields can be multi-line
482
+ tuneBuilder.addMetaTextArray(field, parseDirective.parseFontChangeLine(restOfLine), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
483
+ } else if (field !== undefined) {
484
+ // these fields are single line
485
+ tuneBuilder.addMetaText(field, parseDirective.parseFontChangeLine(restOfLine), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
486
486
  } else {
487
487
  var startChar = multilineVars.iChar;
488
488
  var endChar = startChar + line.length;
489
489
  switch(line[0])
490
490
  {
491
491
  case 'H':
492
- tuneBuilder.addMetaText("history", tokenizer.translateString(tokenizer.stripComment(line.substring(2))), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
492
+ // History is a little different because once it starts it continues until another header field is encountered
493
+ tuneBuilder.addMetaTextArray("history", parseDirective.parseFontChangeLine(restOfLine), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
493
494
  line = tokenizer.peekLine()
494
495
  while (line && line[1] !== ':') {
495
496
  tokenizer.nextLine()
496
- tuneBuilder.addMetaText("history", tokenizer.translateString(tokenizer.stripComment(line)), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
497
+ tuneBuilder.addMetaTextArray("history", parseDirective.parseFontChangeLine(tokenizer.translateString(tokenizer.stripComment(line))), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
497
498
  line = tokenizer.peekLine()
498
499
  }
499
500
  break;
@@ -518,9 +519,9 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
518
519
  case 'P':
519
520
  // TODO-PER: There is more to do with parts, but the writer doesn't care.
520
521
  if (multilineVars.is_in_header)
521
- tuneBuilder.addMetaText("partOrder", tokenizer.translateString(tokenizer.stripComment(line.substring(2))), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
522
+ tuneBuilder.addMetaText("partOrder", parseDirective.parseFontChangeLine(restOfLine), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
522
523
  else
523
- multilineVars.partForNextLine = { title: tokenizer.translateString(tokenizer.stripComment(line.substring(2))), startChar: startChar, endChar: endChar};
524
+ multilineVars.partForNextLine = { title: restOfLine, startChar: startChar, endChar: endChar};
524
525
  break;
525
526
  case 'Q':
526
527
  var tempo = this.setTempo(line, 2, line.length, multilineVars.iChar);
@@ -533,7 +534,9 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
533
534
  }
534
535
  break;
535
536
  case 'T':
536
- this.setTitle(line.substring(2));
537
+ if (multilineVars.titlecaps)
538
+ restOfLine = restOfLine.toUpperCase();
539
+ this.setTitle(parseDirective.parseFontChangeLine(tokenizer.theReverser(restOfLine)), origSize);
537
540
  break;
538
541
  case 'U':
539
542
  this.addUserDefinition(line, 2, line.length);
@@ -639,13 +639,78 @@ var Tokenizer = function(lines, multilineVars) {
639
639
  return {value: num/den, index: index};
640
640
  };
641
641
 
642
- this.theReverser = function(str) {
643
- if (parseCommon.endsWith(str, ", The"))
644
- return "The " + str.substring(0, str.length-5);
645
- if (parseCommon.endsWith(str, ", A"))
646
- return "A " + str.substring(0, str.length-3);
647
- return str;
648
- };
642
+ //
643
+ // MAE 10 Jan 2023 - For better handling of tunes that have tune numbers in front of them.
644
+ //
645
+ // Previous version would take:
646
+ // 21. Woman of the House, The
647
+ // and return:
648
+ // The 21. Woman of the House
649
+ //
650
+ // This fix results in:
651
+ // 21. The Woman of the House
652
+ //
653
+ // Also added additional checks and handlers for lower case ", the" and ", a" since I found several tune collections with those tune name constructs
654
+ //
655
+ // Find an optional title number at the start of a tune title
656
+ function getTitleNumber(str){
657
+
658
+ const regex = /^(\d+)\./;
659
+
660
+ // Use the exec method to search for the pattern in the string
661
+ const match = regex.exec(str);
662
+
663
+ // Check if a match is found
664
+ if (match) {
665
+
666
+ // The matched number is captured in the first group (index 1)
667
+ const foundNumber = match[1];
668
+ return foundNumber;
669
+
670
+ } else {
671
+
672
+ // Return null if no match is found
673
+ return null;
674
+
675
+ }
676
+
677
+ }
678
+
679
+ var thePatterns = [
680
+ { match: /,\s*[Tt]he$/, replace: "The " },
681
+ { match: /,\s*[Aa]$/, replace: "A " },
682
+ { match: /,\s*[Aa]n$/, replace: "An " },
683
+ ]
684
+
685
+ this.theReverser = function (str) {
686
+
687
+ for (var i = 0; i < thePatterns.length; i++) {
688
+ var thisPattern = thePatterns[i]
689
+ var match = str.match(thisPattern.match)
690
+ if (match) {
691
+ var theTitleNumber = getTitleNumber(str);
692
+ if (theTitleNumber){
693
+
694
+ //console.log("theReverser The titlenumber:"+theTitleNumber);
695
+
696
+ str = str.replace(theTitleNumber+".","");
697
+ str = str.trim();
698
+ }
699
+ var len = match[0].length
700
+ var result = thisPattern.replace + str.substring(0, str.length - len);
701
+
702
+ if (theTitleNumber){
703
+ result = theTitleNumber+". "+result;
704
+ }
705
+
706
+ return result;
707
+
708
+ }
709
+ }
710
+
711
+ return str;
712
+
713
+ };
649
714
 
650
715
  this.stripComment = function(str) {
651
716
  var i = str.indexOf('%');
@@ -1,5 +1,6 @@
1
1
  var parseKeyVoice = require('../parse/abc_parse_key_voice');
2
2
  var parseCommon = require('../parse/abc_common');
3
+ var parseDirective = require('./abc_parse_directive');
3
4
 
4
5
  var TuneBuilder = function(tune) {
5
6
  var self = this;
@@ -147,6 +148,9 @@ var TuneBuilder = function(tune) {
147
148
  this.closeLine(); // Close the last line.
148
149
  delete tune.runningFonts;
149
150
 
151
+ simplifyMetaText(tune)
152
+ //addRichTextToAnnotationsAndLyrics(tune)
153
+
150
154
  // 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.
151
155
  if (tune.metaText.tempo && tune.metaText.tempo.bpm && !tune.metaText.tempo.duration)
152
156
  tune.metaText.tempo.duration = [ tune.getBeatLength() ];
@@ -878,7 +882,15 @@ var TuneBuilder = function(tune) {
878
882
  tune.metaText[key] = value;
879
883
  tune.metaTextInfo[key] = info;
880
884
  } else {
881
- tune.metaText[key] += "\n" + value;
885
+ if (typeof tune.metaText[key] === 'string' && typeof value === 'string')
886
+ tune.metaText[key] += "\n" + value;
887
+ else {
888
+ if (tune.metaText[key] === 'string')
889
+ tune.metaText[key] = [{text: tune.metaText[key]}]
890
+ if (typeof value === 'string')
891
+ value = [{text: value}]
892
+ tune.metaText[key] =tune.metaText[key].concat(value)
893
+ }
882
894
  tune.metaTextInfo[key].endChar = info.endChar;
883
895
  }
884
896
  };
@@ -898,4 +910,51 @@ var TuneBuilder = function(tune) {
898
910
  };
899
911
  };
900
912
 
913
+ function isArrayOfStrings(arr) {
914
+ if (!arr) return false
915
+ if (typeof arr === "string") return false
916
+ var str = ''
917
+ for (var i = 0; i < arr.length; i++) {
918
+ if (typeof arr[i] !== 'string')
919
+ return false
920
+ }
921
+ return true
922
+ }
923
+
924
+ function simplifyMetaText(tune) {
925
+ if (isArrayOfStrings(tune.metaText.notes))
926
+ tune.metaText.notes = tune.metaText.notes.join("\n")
927
+ if (isArrayOfStrings(tune.metaText.history))
928
+ tune.metaText.history = tune.metaText.history.join("\n")
929
+ }
930
+
931
+ function addRichTextToAnnotationsAndLyrics(tune) {
932
+ var lines = tune.lines
933
+ for (var i = 0; i < lines.length; i++) {
934
+ if (lines[i].staff !== undefined) {
935
+ for (var s = 0; s < lines[i].staff.length; s++) {
936
+ for (var v = 0; v < lines[i].staff[s].voices.length; v++) {
937
+ var voice = lines[i].staff[s].voices[v];
938
+ for (var n = 0; n < voice.length; n++) {
939
+ var element = voice[n]
940
+ if (element.chord) {
941
+ for (var c = 0; c < element.chord.length; c++) {
942
+ element.chord[c].name = parseDirective.parseFontChangeLine(element.chord[c].name)
943
+ console.log(element.chord[c].name)
944
+ }
945
+ }
946
+ if (element.lyric) {
947
+ for (var l = 0; l < element.lyric.length; l++) {
948
+ element.lyric[l].syllable = parseDirective.parseFontChangeLine(element.lyric[l].syllable)
949
+ console.log(element.lyric[l].syllable)
950
+ }
951
+ }
952
+ }
953
+ }
954
+ }
955
+ }
956
+ }
957
+
958
+ }
959
+
901
960
  module.exports = TuneBuilder;
@@ -21,6 +21,10 @@ function SynthController() {
21
21
  self.load = function (selector, cursorControl, visualOptions) {
22
22
  if (!visualOptions)
23
23
  visualOptions = {};
24
+ if (visualOptions.displayPlay === undefined)
25
+ visualOptions.displayPlay = true
26
+ if (visualOptions.displayProgress === undefined)
27
+ visualOptions.displayProgress = true
24
28
  self.control = new CreateSynthControl(selector, {
25
29
  loopHandler: visualOptions.displayLoop ? self.toggleLoop : undefined,
26
30
  restartHandler: visualOptions.displayRestart ? self.restart : undefined,
@@ -41,7 +45,7 @@ function SynthController() {
41
45
  self.setTune = function(visualObj, userAction, audioParams) {
42
46
  self.visualObj = visualObj;
43
47
  self.disable(false);
44
- self.options = audioParams;
48
+ self.options = audioParams ? audioParams : {};
45
49
 
46
50
  if (self.control) {
47
51
  self.pause();
@@ -195,7 +199,7 @@ function SynthController() {
195
199
 
196
200
  self._randomAccess = function (ev) {
197
201
  var background = (ev.target.classList.contains('abcjs-midi-progress-indicator')) ? ev.target.parentNode : ev.target;
198
- var percent = (ev.x - background.offsetLeft) / background.offsetWidth;
202
+ var percent = (ev.x - background.getBoundingClientRect().left) / background.offsetWidth;
199
203
  if (percent < 0)
200
204
  percent = 0;
201
205
  if (percent > 1)
@@ -265,6 +265,17 @@ StringPatterns.prototype.tabInfos = function (plugin) {
265
265
  return '';
266
266
  };
267
267
 
268
+ // MAE 27 Nov 2023
269
+ StringPatterns.prototype.suppress = function (plugin) {
270
+ var _super = plugin._super;
271
+ var suppress = _super.params.suppress;
272
+ if (suppress){
273
+ return true;
274
+ }
275
+ return false;
276
+ };
277
+ // MAE 27 Nov 2023 End
278
+
268
279
  /**
269
280
  * Common patterns for all string instruments
270
281
  * @param {} plugin
@@ -1,23 +1,23 @@
1
- var StringPatterns = require('../string-patterns');
1
+ var StringPatterns = require('./string-patterns');
2
2
 
3
- function GuitarPatterns(plugin) {
3
+ function TabStringPatterns(plugin, defaultTuning) {
4
4
  this.tuning = plugin._super.params.tuning;
5
5
  if (!this.tuning) {
6
- this.tuning = ['E,', 'A,', 'D', 'G' , 'B' , 'e'];
6
+ this.tuning = defaultTuning;
7
7
  }
8
8
  plugin.tuning = this.tuning;
9
9
  this.strings = new StringPatterns(plugin);
10
10
  }
11
11
 
12
- GuitarPatterns.prototype.notesToNumber = function (notes, graces) {
12
+ TabStringPatterns.prototype.notesToNumber = function (notes, graces) {
13
13
  var converter = this.strings;
14
14
  return converter.notesToNumber(notes, graces);
15
15
  };
16
16
 
17
- GuitarPatterns.prototype.stringToPitch = function (stringNumber) {
17
+ TabStringPatterns.prototype.stringToPitch = function (stringNumber) {
18
18
  var converter = this.strings;
19
19
  return converter.stringToPitch(stringNumber);
20
20
  };
21
21
 
22
22
 
23
- module.exports = GuitarPatterns;
23
+ module.exports = TabStringPatterns;
@@ -1,8 +1,8 @@
1
1
 
2
- var StringTablature = require('../string-tablature');
3
- var TabCommon = require('../../tab-common');
4
- var TabRenderer = require('../../tab-renderer');
5
- var ViolinPatterns = require('./violin-patterns');
2
+ var StringTablature = require('./string-tablature');
3
+ var TabCommon = require('../tab-common');
4
+ var TabRenderer = require('../tab-renderer');
5
+ var TabStringPatterns = require('./tab-string-patterns');
6
6
 
7
7
 
8
8
  /**
@@ -11,18 +11,20 @@ var ViolinPatterns = require('./violin-patterns');
11
11
  * @param {*} tuneNumber the parsed tune AST tree
12
12
  * @param {*} params complementary args provided to Tablature Plugin
13
13
  */
14
- Plugin.prototype.init = function (abcTune, tuneNumber, params) {
14
+ Plugin.prototype.init = function (abcTune, tuneNumber, params, staffNumber, tabSettings) {
15
15
  var _super = new TabCommon(abcTune, tuneNumber, params);
16
16
  this.abcTune = abcTune;
17
17
  this._super = _super;
18
18
  this.linePitch = 3;
19
- this.nbLines = 4;
20
- this.isTabBig = false;
19
+ this.nbLines = tabSettings.defaultTuning.length;
20
+ this.isTabBig = tabSettings.isTabBig;
21
+ this.tabSymbolOffset = tabSettings.tabSymbolOffset;
21
22
  this.capo = params.capo;
22
23
  this.transpose = params.visualTranspose;
24
+ this.hideTabSymbol = params.hideTabSymbol;
23
25
  this.tablature = new StringTablature(this.nbLines,
24
26
  this.linePitch);
25
- var semantics = new ViolinPatterns(this);
27
+ var semantics = new TabStringPatterns(this, tabSettings.defaultTuning);
26
28
  this.semantics = semantics;
27
29
  };
28
30
 
@@ -38,8 +40,8 @@ function Plugin() {}
38
40
  //
39
41
  // Tablature plugin definition
40
42
  //
41
- var AbcViolinTab = function () {
42
- return { name: 'ViolinTab', tablature: Plugin };
43
+ var AbcStringTab = function () {
44
+ return { name: 'StringTab', tablature: Plugin };
43
45
  };
44
46
 
45
- module.exports = AbcViolinTab;
47
+ module.exports = AbcStringTab;
@@ -60,13 +60,22 @@ function buildTabAbsolute(plugin, absX, relX) {
60
60
  icon: tabIcon,
61
61
  Ypos: tabYPos
62
62
  };
63
- var tabAbsolute = new AbsoluteElement(element, 0, 0, "symbol", 0);
64
- tabAbsolute.x = absX;
65
- var tabRelative = new RelativeElement(tabIcon, 0, 0, 7.5, "tab");
66
- tabRelative.x = relX;
67
- tabAbsolute.children.push(tabRelative);
68
- if (tabAbsolute.abcelem.el_type == 'tab') {
69
- tabRelative.pitch = tabYPos;
63
+
64
+ // Offset the TAB symbol position if specified in the tab description
65
+ tabYPos += plugin.tabSymbolOffset;
66
+
67
+ // For tablature like whistle tab where you want the TAB symbol hidden
68
+ if (!plugin.hideTabSymbol){
69
+
70
+ var tabAbsolute = new AbsoluteElement(element, 0, 0, "symbol", 0);
71
+ tabAbsolute.x = absX;
72
+ var tabRelative = new RelativeElement(tabIcon, 0, 0, 7.5, "tab");
73
+ tabRelative.x = relX;
74
+ tabAbsolute.children.push(tabRelative);
75
+ if (tabAbsolute.abcelem.el_type == 'tab') {
76
+ tabRelative.pitch = tabYPos;
77
+ }
78
+
70
79
  }
71
80
  return tabAbsolute;
72
81
  }