abcjs 6.1.9 → 6.2.1

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 (135) hide show
  1. package/LICENSE.md +1 -1
  2. package/RELEASE.md +58 -0
  3. package/dist/abcjs-basic-min.js +2 -2
  4. package/dist/abcjs-basic-min.js.LICENSE +1 -1
  5. package/dist/abcjs-basic.js +4505 -4556
  6. package/dist/abcjs-basic.js.map +1 -1
  7. package/dist/abcjs-plugin-min.js +2 -2
  8. package/dist/abcjs-plugin-min.js.LICENSE +1 -1
  9. package/index.js +2 -2
  10. package/license.js +1 -1
  11. package/package.json +1 -1
  12. package/plugin.js +2 -2
  13. package/src/api/abc_timing_callbacks.js +8 -4
  14. package/src/api/abc_tunebook_svg.js +1 -2
  15. package/src/data/abc_tune.js +3 -3
  16. package/src/parse/abc_common.js +0 -47
  17. package/src/parse/abc_parse.js +16 -16
  18. package/src/parse/abc_parse_book.js +3 -3
  19. package/src/parse/abc_parse_directive.js +26 -7
  20. package/src/parse/abc_parse_header.js +11 -9
  21. package/src/parse/abc_parse_key_voice.js +17 -17
  22. package/src/parse/abc_parse_music.js +88 -105
  23. package/src/parse/abc_tokenizer.js +60 -60
  24. package/src/parse/tune-builder.js +19 -14
  25. package/src/synth/abc_midi_flattener.js +25 -9
  26. package/src/synth/create-synth.js +71 -4
  27. package/src/synth/load-note.js +1 -1
  28. package/src/synth/note-to-midi.js +50 -0
  29. package/src/synth/place-note.js +10 -2
  30. package/src/tablatures/instruments/guitar/tab-guitar.js +0 -2
  31. package/src/tablatures/instruments/string-patterns.js +47 -29
  32. package/src/tablatures/instruments/tab-note.js +26 -103
  33. package/src/tablatures/instruments/violin/tab-violin.js +0 -2
  34. package/src/tablatures/tab-absolute-elements.js +9 -31
  35. package/src/tablatures/tab-renderer.js +2 -2
  36. package/src/test/abc_parser_lint.js +7 -4
  37. package/src/write/README.md +31 -0
  38. package/src/write/creation/abstract-engraver.js +1036 -0
  39. package/src/write/creation/add-chord.js +102 -0
  40. package/src/write/{add-text-if.js → creation/add-text-if.js} +6 -6
  41. package/src/write/{calcHeight.js → creation/calc-height.js} +2 -2
  42. package/src/write/creation/create-clef.js +72 -0
  43. package/src/write/creation/create-key-signature.js +31 -0
  44. package/src/write/creation/create-note-head.js +107 -0
  45. package/src/write/creation/create-time-signature.js +55 -0
  46. package/src/write/creation/decoration.js +357 -0
  47. package/src/write/{abc_absolute_element.js → creation/elements/absolute-element.js} +14 -15
  48. package/src/write/creation/elements/beam-element.js +113 -0
  49. package/src/write/{bottom-text.js → creation/elements/bottom-text.js} +14 -15
  50. package/src/write/{abc_brace_element.js → creation/elements/brace-element.js} +5 -5
  51. package/src/write/creation/elements/free-text.js +41 -0
  52. package/src/write/{abc_relative_element.js → creation/elements/relative-element.js} +7 -7
  53. package/src/write/{separator.js → creation/elements/separator.js} +2 -2
  54. package/src/write/{abc_staff_group_element.js → creation/elements/staff-group-element.js} +4 -4
  55. package/src/write/{subtitle.js → creation/elements/subtitle.js} +3 -3
  56. package/src/write/creation/elements/tempo-element.js +63 -0
  57. package/src/write/{abc_tie_element.js → creation/elements/tie-element.js} +15 -11
  58. package/src/write/{top-text.js → creation/elements/top-text.js} +12 -12
  59. package/src/write/creation/elements/triplet-element.js +28 -0
  60. package/src/write/{abc_voice_element.js → creation/elements/voice-element.js} +3 -3
  61. package/src/write/creation/glyphs.js +226 -0
  62. package/src/write/creation/translate-chord.js +37 -0
  63. package/src/write/draw/absolute.js +5 -5
  64. package/src/write/draw/beam.js +8 -8
  65. package/src/write/draw/brace.js +33 -33
  66. package/src/write/draw/crescendo.js +4 -4
  67. package/src/write/draw/debug-box.js +1 -1
  68. package/src/write/draw/draw.js +7 -7
  69. package/src/write/draw/dynamics.js +2 -2
  70. package/src/write/draw/ending.js +6 -6
  71. package/src/write/draw/glissando.js +17 -17
  72. package/src/write/draw/group-elements.js +51 -51
  73. package/src/write/draw/horizontal-line.js +9 -9
  74. package/src/write/draw/non-music.js +1 -1
  75. package/src/write/draw/print-line.js +25 -16
  76. package/src/write/draw/print-stem.js +15 -5
  77. package/src/write/draw/print-symbol.js +12 -12
  78. package/src/write/draw/print-vertical-line.js +8 -8
  79. package/src/write/draw/relative.js +17 -16
  80. package/src/write/draw/selectables.js +5 -5
  81. package/src/write/draw/separator.js +4 -4
  82. package/src/write/draw/set-paper-size.js +2 -2
  83. package/src/write/draw/sprintf.js +31 -31
  84. package/src/write/draw/staff-group.js +36 -30
  85. package/src/write/draw/staff-line.js +2 -2
  86. package/src/write/draw/staff.js +4 -4
  87. package/src/write/draw/tab-line.js +26 -26
  88. package/src/write/draw/tempo.js +30 -30
  89. package/src/write/draw/text.js +5 -5
  90. package/src/write/draw/tie.js +18 -18
  91. package/src/write/draw/triplet.js +6 -6
  92. package/src/write/draw/voice.js +16 -17
  93. package/src/write/{abc_engraver_controller.js → engraver-controller.js} +58 -51
  94. package/src/write/{classes.js → helpers/classes.js} +6 -6
  95. package/src/write/{get-font-and-attr.js → helpers/get-font-and-attr.js} +9 -7
  96. package/src/write/{get-text-size.js → helpers/get-text-size.js} +5 -5
  97. package/src/write/{set-class.js → helpers/set-class.js} +1 -1
  98. package/src/write/{abc_spacing.js → helpers/spacing.js} +1 -1
  99. package/src/write/{highlight.js → interactive/highlight.js} +1 -1
  100. package/src/write/{selection.js → interactive/selection.js} +34 -31
  101. package/src/write/{unhighlight.js → interactive/unhighlight.js} +1 -1
  102. package/src/write/layout/beam.js +13 -13
  103. package/src/write/layout/get-left-edge-of-staff.js +4 -4
  104. package/src/write/layout/layout.js +74 -74
  105. package/src/write/layout/{setUpperAndLowerElements.js → set-upper-and-lower-elements.js} +8 -8
  106. package/src/write/layout/{staffGroup.js → staff-group.js} +32 -32
  107. package/src/write/layout/triplet.js +4 -4
  108. package/src/write/layout/{VoiceElements.js → voice-elements.js} +23 -23
  109. package/src/write/layout/voice.js +6 -6
  110. package/src/write/{abc_renderer.js → renderer.js} +36 -32
  111. package/src/write/svg.js +35 -35
  112. package/test.js +2 -2
  113. package/types/index.d.ts +37 -8
  114. package/version.js +1 -1
  115. package/src/tablatures/instruments/guitar/guitar-fonts.js +0 -19
  116. package/src/tablatures/instruments/violin/violin-fonts.js +0 -19
  117. package/src/tablatures/transposer.js +0 -110
  118. package/src/write/abc_abstract_engraver.js +0 -1026
  119. package/src/write/abc_beam_element.js +0 -113
  120. package/src/write/abc_create_clef.js +0 -72
  121. package/src/write/abc_create_key_signature.js +0 -33
  122. package/src/write/abc_create_note_head.js +0 -107
  123. package/src/write/abc_create_time_signature.js +0 -55
  124. package/src/write/abc_decoration.js +0 -357
  125. package/src/write/abc_glyphs.js +0 -224
  126. package/src/write/abc_tempo_element.js +0 -63
  127. package/src/write/abc_triplet_element.js +0 -28
  128. package/src/write/add-chord.js +0 -103
  129. package/src/write/format-jazz-chord.js +0 -15
  130. package/src/write/free-text.js +0 -41
  131. /package/src/write/{abc_crescendo_element.js → creation/elements/crescendo-element.js} +0 -0
  132. /package/src/write/{abc_dynamic_decoration.js → creation/elements/dynamic-decoration.js} +0 -0
  133. /package/src/write/{abc_ending_element.js → creation/elements/ending-element.js} +0 -0
  134. /package/src/write/{abc_glissando_element.js → creation/elements/glissando-element.js} +0 -0
  135. /package/src/write/layout/{getBarYAt.js → get-bar-y-at.js} +0 -0
@@ -26,11 +26,13 @@ function CreateSynth() {
26
26
  self.audioBuffers = []; // cache of the buffers so starting play can be fast.
27
27
  self.duration = undefined; // the duration of the tune in seconds.
28
28
  self.isRunning = false; // whether there is currently a sound buffer running.
29
+ // self.options = undefined
29
30
 
30
31
  // Load and cache all needed sounds
31
32
  self.init = function(options) {
32
33
  if (!options)
33
34
  options = {};
35
+ // self.options = options
34
36
  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.
35
37
  var startTime = activeAudioContext().currentTime;
36
38
  self.debugCallback = options.debugCallback;
@@ -163,6 +165,9 @@ function CreateSynth() {
163
165
  notes.push({ instrument: instrument, note: note });
164
166
  });
165
167
  });
168
+ if (self.debugCallback)
169
+ self.debugCallback("notes "+JSON.stringify(notes));
170
+
166
171
  // If there are lots of notes, load them in batches
167
172
  var batches = [];
168
173
  var CHUNK = 256;
@@ -179,8 +184,13 @@ function CreateSynth() {
179
184
 
180
185
  var index = 0;
181
186
  var next = function() {
187
+ if (self.debugCallback)
188
+ self.debugCallback("loadBatch idx="+index+ " len="+batches.length);
189
+
182
190
  if (index < batches.length) {
183
191
  self._loadBatch(batches[index], self.soundFontUrl, startTime).then(function(data) {
192
+ if (self.debugCallback)
193
+ self.debugCallback("loadBatch then");
184
194
  startTime = activeAudioContext().currentTime;
185
195
  if (data) {
186
196
  if (data.error)
@@ -192,6 +202,9 @@ function CreateSynth() {
192
202
  next();
193
203
  }, reject);
194
204
  } else {
205
+ if (self.debugCallback)
206
+ self.debugCallback("resolve init");
207
+
195
208
  resolve(results);
196
209
  }
197
210
  };
@@ -203,6 +216,8 @@ function CreateSynth() {
203
216
  // This is called recursively to see if the sounds have loaded. The "delay" parameter is how long it has been since the original call.
204
217
  var promises = [];
205
218
  batch.forEach(function(item) {
219
+ if (self.debugCallback)
220
+ self.debugCallback("getNote " + item.instrument+':'+item.note);
206
221
  promises.push(getNote(soundFontUrl, item.instrument, item.note, activeAudioContext()));
207
222
  });
208
223
  return Promise.all(promises).then(function(response) {
@@ -225,6 +240,8 @@ function CreateSynth() {
225
240
  error.push(which + ' ' + oneResponse.message);
226
241
  }
227
242
  if (pending.length > 0) {
243
+ if (self.debugCallback)
244
+ self.debugCallback("pending " + JSON.stringify(pending));
228
245
  // There was probably a second call for notes before the first one finished, so just retry a few times to see if they stop being pending.
229
246
  // Retry quickly at first so that there isn't an unnecessary delay, but increase the delay each time.
230
247
  if (!delay)
@@ -239,7 +256,9 @@ function CreateSynth() {
239
256
  which = pending[i].split(":");
240
257
  newBatch.push({instrument: which[0], note: which[1]});
241
258
  }
242
- self._loadBatch(newBatch, soundFontUrl, startTime, delay).then(function (response) {
259
+ if (self.debugCallback)
260
+ self.debugCallback("retry " + JSON.stringify(newBatch));
261
+ self._loadBatch(newBatch, soundFontUrl, startTime, delay).then(function (response) {
243
262
  resolve(response);
244
263
  }).catch(function (error) {
245
264
  reject(error);
@@ -250,11 +269,18 @@ function CreateSynth() {
250
269
  var list = [];
251
270
  for (var j = 0; j < batch.length; j++)
252
271
  list.push(batch[j].instrument+'/'+batch[j].note)
253
- return Promise.reject(new Error("timeout attempting to load: " + list.join(", ")));
272
+ if (self.debugCallback)
273
+ self.debugCallback("loadBatch timeout")
274
+ return Promise.reject(new Error("timeout attempting to load: " + list.join(", ")));
254
275
  }
255
- } else
276
+ } else {
277
+ if (self.debugCallback)
278
+ self.debugCallback("loadBatch resolve")
256
279
  return Promise.resolve({loaded: loaded, cached: cached, error: error});
280
+ }
257
281
  }).catch(function (error) {
282
+ if (self.debugCallback)
283
+ self.debugCallback("loadBatch catch "+error.message)
258
284
  });
259
285
  });
260
286
 
@@ -283,6 +309,8 @@ function CreateSynth() {
283
309
  self.stop();
284
310
 
285
311
  var noteMapTracks = createNoteMap(self.flattened);
312
+ // if (self.options.swing)
313
+ // addSwing(noteMapTracks, self.options.swing, self.beatsPerMeasure)
286
314
  if (self.sequenceCallback)
287
315
  self.sequenceCallback(noteMapTracks, self.callbackContext);
288
316
 
@@ -295,6 +323,8 @@ function CreateSynth() {
295
323
  var panDistance = panDistances && panDistances.length > trackNumber ? panDistances[trackNumber] : 0;
296
324
  noteMap.forEach(function(note) {
297
325
  var key = note.instrument + ':' + note.pitch + ':' +note.volume + ':' + Math.round((note.end-note.start)*1000)/1000 + ':' + panDistance + ':' + tempoMultiplier + ':' + (note.cents ? note.cents : 0);
326
+ if (self.debugCallback)
327
+ self.debugCallback("noteMapTrack "+key)
298
328
  if (!uniqueSounds[key])
299
329
  uniqueSounds[key] = [];
300
330
  uniqueSounds[key].push(note.start);
@@ -309,7 +339,7 @@ function CreateSynth() {
309
339
  var parts = k.split(":");
310
340
  var cents = parts[6] !== undefined ? parseFloat(parts[6]) : 0;
311
341
  parts = {instrument: parts[0], pitch: parseInt(parts[1], 10), volume: parseInt(parts[2], 10), len: parseFloat(parts[3]), pan: parseFloat(parts[4]), tempoMultiplier: parseFloat(parts[5]), cents: cents};
312
- allPromises.push(placeNote(audioBuffer, activeAudioContext().sampleRate, parts, uniqueSounds[k], self.soundFontVolumeMultiplier, self.programOffsets[parts.instrument], fadeTimeSec, self.noteEnd/1000));
342
+ allPromises.push(placeNote(audioBuffer, activeAudioContext().sampleRate, parts, uniqueSounds[k], self.soundFontVolumeMultiplier, self.programOffsets[parts.instrument], fadeTimeSec, self.noteEnd/1000, self.debugCallback));
313
343
  }
314
344
  self.audioBuffers = [audioBuffer];
315
345
 
@@ -477,6 +507,10 @@ function CreateSynth() {
477
507
  return downloadBuffer(self);
478
508
  };
479
509
 
510
+ self.getAudioBuffer = function() {
511
+ return self.audioBuffers[0];
512
+ };
513
+
480
514
  /////////////// Private functions //////////////
481
515
 
482
516
  self._deviceCapable = function() {
@@ -506,6 +540,39 @@ function CreateSynth() {
506
540
  };
507
541
  }
508
542
  };
543
+
544
+ // // this is a first attempt at adding a little bit of swing to the output, but the algorithm isn't correct.
545
+ // function addSwing(noteMapTracks, swing, beatsPerMeasure) {
546
+ // console.log("addSwing", noteMapTracks, swing, beatsPerMeasure)
547
+ // // Swing should be between -0.9 and 0.9. Make sure the input is between them.
548
+ // // Then that is the percentage to add to the first beat, so a negative number moves the second beat earlier.
549
+ // // A value of zero is the same as no swing at all.
550
+ // // This only works when there are an even number of beats in a measure.
551
+ // if (beatsPerMeasure % 2 !== 0)
552
+ // return;
553
+ // swing = parseFloat(swing)
554
+ // if (isNaN(swing))
555
+ // return
556
+ // if (swing < -0.9)
557
+ // swing = -0.9
558
+ // if (swing > 0.9)
559
+ // swing = 0.9
560
+ // var beatLength = (1 / beatsPerMeasure)*2
561
+ // swing = beatLength * swing
562
+ // for (var t = 0; t < noteMapTracks.length; t++) {
563
+ // var track = noteMapTracks[t];
564
+ // for (var i = 0; i < track.length; i++) {
565
+ // var event = track[i];
566
+ // if (event.start % beatLength) {
567
+ // // This is the off beat
568
+ // event.start += swing;
569
+ // } else {
570
+ // // This is the beat
571
+ // event.end += swing;
572
+ // }
573
+ // }
574
+ // }
575
+ // }
509
576
  }
510
577
 
511
578
  module.exports = CreateSynth;
@@ -16,7 +16,7 @@ var getNote = function (url, instrument, name, audioContext) {
16
16
  xhr.responseType = "arraybuffer";
17
17
  xhr.onload = function () {
18
18
  if (xhr.status !== 200) {
19
- reject(Error("Can't load sound at " + noteUrl));
19
+ reject(Error("Can't load sound at " + noteUrl + ' status=' + xhr.status));
20
20
  return
21
21
  }
22
22
  var noteDecoded = function(audioBuffer) {
@@ -0,0 +1,50 @@
1
+ var accidentals = {
2
+ "__": -2,
3
+ "_": -1,
4
+ "_/": -0.5,
5
+ "=": 0,
6
+ "": 0,
7
+ "^/": 0.5,
8
+ "^": 1,
9
+ "^^": 2
10
+ }
11
+
12
+ var notesInOrder = ['C', '-', 'D', '-', 'E', 'F', '-', 'G', '-', 'A', '-', 'B', 'c', '-', 'd', '-', 'e', 'f', '-', 'g', '-', 'a', '-', 'b']
13
+
14
+ function noteToMidi(note) {
15
+ var reg = note.match(/([_^\/]*)([ABCDEFGabcdefg])(,*)('*)/)
16
+ if (reg && reg.length === 5) {
17
+ var acc = accidentals[reg[1]]
18
+ var pitch = notesInOrder.indexOf(reg[2])
19
+ var octave = reg[4].length - reg[3].length
20
+ return 48 + pitch + acc + octave * 12;
21
+ }
22
+ return 0;
23
+ }
24
+
25
+ function midiToNote(midi) {
26
+ midi = parseInt(midi, 10) // TODO-PER: not sure how to handle quarter sharps and flats, so strip them for now.
27
+ var octave = Math.floor(midi / 12)
28
+ var pitch = midi % 12
29
+ var name = notesInOrder[pitch]
30
+ if (name === '-') {
31
+ name = '^' + notesInOrder[pitch-1]
32
+ }
33
+
34
+ if (octave > 4) {
35
+ name = name.toLowerCase()
36
+ octave -= 5
37
+ while (octave > 0) {
38
+ name += "'"
39
+ octave--
40
+ }
41
+ } else {
42
+ while (octave < 4) {
43
+ name += ','
44
+ octave++
45
+ }
46
+ }
47
+ return name
48
+ }
49
+
50
+ module.exports = {noteToMidi: noteToMidi, midiToNote: midiToNote};
@@ -2,7 +2,7 @@ var soundsCache = require('./sounds-cache');
2
2
  var pitchToNoteName = require('./pitch-to-note-name');
3
3
  var centsToFactor = require("./cents-to-factor");
4
4
 
5
- function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMultiplier, ofsMs, fadeTimeSec, noteEndSec) {
5
+ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMultiplier, ofsMs, fadeTimeSec, noteEndSec, debugCallback) {
6
6
  // sound contains { instrument, pitch, volume, len, pan, tempoMultiplier
7
7
  // len is in whole notes. Multiply by tempoMultiplier to get seconds.
8
8
  // ofsMs is an offset to subtract from the note to line up programs that have different length onsets.
@@ -21,6 +21,8 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
21
21
 
22
22
  if (!noteBufferPromise) {
23
23
  // if the note isn't present then just skip it - it will leave a blank spot in the audio.
24
+ if (debugCallback)
25
+ debugCallback('placeNote skipped: '+sound.instrument+':'+noteName)
24
26
  return Promise.resolve();
25
27
  }
26
28
 
@@ -81,6 +83,8 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
81
83
  copyToChannel(outputAudioBuffer, e.renderedBuffer, start);
82
84
  }
83
85
  }
86
+ if (debugCallback)
87
+ debugCallback('placeNote: '+sound.instrument+':'+noteName)
84
88
  fnResolve();
85
89
  };
86
90
  offlineCtx.startRendering();
@@ -88,7 +92,11 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
88
92
  fnResolve = resolve;
89
93
  });
90
94
  })
91
- .catch(function () {});
95
+ .catch(function (error) {
96
+ if (debugCallback)
97
+ debugCallback('placeNote catch: '+error.message)
98
+ return Promise.resolve()
99
+ });
92
100
  }
93
101
 
94
102
  var copyToChannel = function(toBuffer, fromBuffer, start) {
@@ -5,7 +5,6 @@ var StringTablature = require('../string-tablature');
5
5
  var TabCommon = require('../../tab-common');
6
6
  var TabRenderer = require('../../tab-renderer');
7
7
  var GuitarPatterns = require('./guitar-patterns');
8
- var setGuitarFonts = require('./guitar-fonts');
9
8
 
10
9
  /**
11
10
  * upon init mainly store provided instances for later usage
@@ -32,7 +31,6 @@ Plugin.prototype.init = function (abcTune, tuneNumber, params) {
32
31
  Plugin.prototype.render = function (renderer, line, staffIndex) {
33
32
  if (this._super.inError) return;
34
33
  if (this.tablature.bypass(line)) return;
35
- setGuitarFonts(this.abcTune);
36
34
  var rndrer = new TabRenderer(this, renderer, line, staffIndex);
37
35
  rndrer.doLayout();
38
36
  };
@@ -1,3 +1,4 @@
1
+ const {noteToMidi} = require('../../synth/note-to-midi');
1
2
  var TabNote = require('./tab-note');
2
3
  var TabNotes = require('./tab-notes');
3
4
 
@@ -93,7 +94,10 @@ function sameString(self, chord) {
93
94
  function handleChordNotes(self, notes) {
94
95
  var retNotes = [];
95
96
  for (var iiii = 0; iiii < notes.length; iiii++) {
96
- var note = new TabNote.TabNote(notes[iiii].name);
97
+ if (notes[iiii].endTie)
98
+ continue;
99
+ var note = new TabNote.TabNote(notes[iiii].name, self.clefTranspose);
100
+ note.checkKeyAccidentals(self.accidentals, self.measureAccidentals)
97
101
  var curPos = toNumber(self, note);
98
102
  retNotes.push(curPos);
99
103
  }
@@ -147,26 +151,23 @@ function toNumber(self, note) {
147
151
  acc = "="
148
152
  self.measureAccidentals[note.name.toUpperCase()] = acc
149
153
  }
150
- var num = null;
151
- var str = 0;
152
- var lowestString = self.strings[self.strings.length - 1];
153
- var lowestNote = new TabNote.TabNote(lowestString[0]);
154
- if (note.isLowerThan(lowestNote) ) {
155
- return {
156
- num: "?",
157
- str: self.strings.length - 1,
158
- note: note,
159
- error: note.emit() + ': unexpected note for instrument'
160
- };
161
- }
162
- while (str < self.strings.length) {
163
- num = noteToNumber(self, note, str);
164
- if (num) {
165
- return num;
154
+ for (var i = self.stringPitches.length-1; i >= 0; i--) {
155
+ if (note.pitch + note.pitchAltered >= self.stringPitches[i]) {
156
+ var num = note.pitch + note.pitchAltered - self.stringPitches[i]
157
+ if (note.quarter === '^') num -= 0.5
158
+ else if (note.quarter === "v") num += 0.5
159
+ return {
160
+ num: Math.round(num),
161
+ str: self.stringPitches.length-1-i, // reverse the strings because string 0 is on the bottom
162
+ note: note
163
+ }
166
164
  }
167
- str++;
168
165
  }
169
- return null; // not found
166
+ return {
167
+ num: "?",
168
+ str: self.stringPitches.length-1,
169
+ note: note,
170
+ };
170
171
  }
171
172
 
172
173
  StringPatterns.prototype.stringToPitch = function (stringNumber) {
@@ -198,13 +199,16 @@ StringPatterns.prototype.notesToNumber = function (notes, graces) {
198
199
  error = retNotes.error;
199
200
  }
200
201
  } else {
201
- note = new TabNote.TabNote(notes[0].name);
202
- number = toNumber(this, note);
203
- if (number) {
204
- retNotes.push(number);
205
- } else {
206
- invalidNumber(retNotes, note);
207
- error = retNotes.error;
202
+ if (!notes[0].endTie) {
203
+ note = new TabNote.TabNote(notes[0].name, this.clefTranspose);
204
+ note.checkKeyAccidentals(this.accidentals, this.measureAccidentals)
205
+ number = toNumber(this, note);
206
+ if (number) {
207
+ retNotes.push(number);
208
+ } else {
209
+ invalidNumber(retNotes, note);
210
+ error = retNotes.error;
211
+ }
208
212
  }
209
213
  }
210
214
  }
@@ -213,7 +217,8 @@ StringPatterns.prototype.notesToNumber = function (notes, graces) {
213
217
  if (graces) {
214
218
  retGraces = [];
215
219
  for (var iiii = 0; iiii < graces.length; iiii++) {
216
- note = new TabNote.TabNote(graces[iiii].name);
220
+ note = new TabNote.TabNote(graces[iiii].name, this.clefTranspose);
221
+ note.checkKeyAccidentals(this.accidentals, this.measureAccidentals)
217
222
  number = toNumber(this, note);
218
223
  if (number) {
219
224
  retGraces.push(number);
@@ -232,7 +237,14 @@ StringPatterns.prototype.notesToNumber = function (notes, graces) {
232
237
  };
233
238
 
234
239
  StringPatterns.prototype.toString = function () {
235
- return this.tuning.join('').replaceAll(',', '').toUpperCase();
240
+ var arr = []
241
+ for (var i = 0; i < this.tuning.length; i++) {
242
+ var str = this.tuning[i].replaceAll(',', '').replaceAll("'", '').toUpperCase();
243
+ if (str[0] === '_') str = str[1] + 'b '
244
+ else if (str[0] === '^') str = str[1] + "# "
245
+ arr.push(str)
246
+ }
247
+ return arr.join('');
236
248
  };
237
249
 
238
250
  StringPatterns.prototype.tabInfos = function (plugin) {
@@ -273,9 +285,15 @@ function StringPatterns(plugin) {
273
285
  this.measureAccidentals = {}
274
286
  this.capo = 0;
275
287
  if (capo) {
276
- this.capo = capo;
288
+ this.capo = parseInt(capo,10);
277
289
  }
290
+ this.transpose = plugin.transpose ? plugin.transpose : 0
278
291
  this.tuning = tuning;
292
+ this.stringPitches = []
293
+ for (var i = 0; i < this.tuning.length; i++) {
294
+ var pitch = noteToMidi(this.tuning[i]) + this.capo
295
+ this.stringPitches.push(pitch)
296
+ }
279
297
  if (this.capo > 0) {
280
298
  this.capoTuning = buildCapo(this);
281
299
  }
@@ -1,3 +1,5 @@
1
+ var {noteToMidi, midiToNote} = require('../../synth/note-to-midi');
2
+
1
3
  /**
2
4
  *
3
5
  * Note structure for Tabs
@@ -6,9 +8,12 @@
6
8
  var notes = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
7
9
 
8
10
 
9
- function TabNote(note) {
11
+ function TabNote(note, clefTranspose) {
12
+ var pitch = noteToMidi(note)
13
+ if (clefTranspose)
14
+ pitch += clefTranspose
15
+ var newNote = midiToNote(pitch);
10
16
  var isFlat = false;
11
- var newNote = note;
12
17
  var isSharp = false;
13
18
  var isAltered = false;
14
19
  var natural = null;
@@ -54,9 +59,11 @@ function TabNote(note) {
54
59
  newNote = note.slice(1);
55
60
  }
56
61
  }
57
- var hasComma = (note.match(/,/g) || []).length;
58
- var hasQuote = (note.match(/'/g) || []).length;
62
+ var hasComma = (newNote.match(/,/g) || []).length;
63
+ var hasQuote = (newNote.match(/'/g) || []).length;
59
64
 
65
+ this.pitch = pitch
66
+ this.pitchAltered = 0
60
67
  this.name = newNote;
61
68
  this.acc = acc;
62
69
  this.isSharp = isSharp;
@@ -76,6 +83,7 @@ function TabNote(note) {
76
83
  function cloneNote(self) {
77
84
  var newNote = self.name;
78
85
  var newTabNote = new TabNote(newNote);
86
+ newTabNote.pitch = self.pitch;
79
87
  newTabNote.hasComma = self.hasComma;
80
88
  newTabNote.isLower = self.isLower;
81
89
  newTabNote.isQuoted = self.isQuoted;
@@ -86,33 +94,11 @@ function cloneNote(self) {
86
94
  return newTabNote;
87
95
  }
88
96
  TabNote.prototype.sameNoteAs = function (note) {
89
- if ((this.name == note.name) &&
90
- (this.hasComma == note.hasComma) &&
91
- (this.isLower == note.isLower) &&
92
- (this.isQuoted == note.isQuoted) &&
93
- (this.isSharp == note.isSharp) &&
94
- (this.isFlat == note.isFlat)) {
95
- return true;
96
- } else {
97
- return false;
98
- }
97
+ return note.pitch === this.pitch
99
98
  };
100
99
 
101
100
  TabNote.prototype.isLowerThan = function (note) {
102
- var noteComparator = ['C','D','E','F','G','A','B'];
103
- if (this.hasComma > note.hasComma) return true;
104
- if (note.hasComma > this.hasComma) return false;
105
- if (this.isQuoted > note.isQuoted) return false;
106
- if (note.isQuoted > this.isQuoted) return true;
107
- if (this.isLower) {
108
- if (!note.isLower) return false;
109
- } else {
110
- if (note.isLower) return true;
111
- }
112
- var noteName = note.name[0].toUpperCase();
113
- var thisName = this.name[0].toUpperCase();
114
- if (noteComparator.indexOf(thisName) < noteComparator.indexOf(noteName)) return true;
115
- return false;
101
+ return note.pitch > this.pitch
116
102
  };
117
103
 
118
104
  TabNote.prototype.checkKeyAccidentals = function(accidentals, measureAccidentals) {
@@ -120,14 +106,13 @@ TabNote.prototype.checkKeyAccidentals = function(accidentals, measureAccidentals
120
106
  return
121
107
  if (measureAccidentals[this.name.toUpperCase()]) {
122
108
  switch (measureAccidentals[this.name.toUpperCase()]) {
123
- case "__": this.acc = -2; return;
124
- case "_": this.acc = -1; return;
125
- case "=": this.acc = 0; return;
126
- case "^": this.acc = 1; return;
127
- case "^^": this.acc = 2; return;
109
+ case "__": this.acc = -2; this.pitchAltered = -2; return;
110
+ case "_": this.acc = -1; this.pitchAltered = -1; return;
111
+ case "=": this.acc = 0; this.pitchAltered = 0; return;
112
+ case "^": this.acc = 1; this.pitchAltered = 1; return;
113
+ case "^^": this.acc = 2; this.pitchAltered = 2; return;
128
114
  }
129
- }
130
- if (accidentals) {
115
+ } else if (accidentals) {
131
116
  var curNote = this.name;
132
117
  for (var iii = 0; iii < accidentals.length; iii++) {
133
118
  var curAccidentals = accidentals[iii];
@@ -135,10 +120,12 @@ TabNote.prototype.checkKeyAccidentals = function(accidentals, measureAccidentals
135
120
  if (curAccidentals.acc == 'flat') {
136
121
  this.acc = -1;
137
122
  this.isKeyFlat = true;
123
+ this.pitchAltered = -1
138
124
  }
139
125
  if (curAccidentals.acc == 'sharp') {
140
126
  this.acc = +1;
141
127
  this.isKeySharp = true;
128
+ this.pitchAltered = 1
142
129
  }
143
130
  }
144
131
  }
@@ -163,77 +150,13 @@ TabNote.prototype.getAccidentalEquiv = function () {
163
150
 
164
151
 
165
152
  TabNote.prototype.nextNote = function () {
166
- var newTabNote = cloneNote(this);
167
-
168
- if (!this.isSharp && !this.isKeySharp ) {
169
- if (this.name != 'E' && this.name != 'B') {
170
- newTabNote.isSharp = true;
171
- return newTabNote;
172
- }
173
- } else {
174
- // cleanup
175
- newTabNote.isSharp = false;
176
- newTabNote.isKeySharp = false;
177
- }
178
- var noteIndex = notes.indexOf(this.name);
179
- if (noteIndex == notes.length - 1) {
180
- noteIndex = 0;
181
- } else {
182
- noteIndex++;
183
- }
184
- newTabNote.name = notes[noteIndex];
185
- if (newTabNote.name == 'C') {
186
- if (newTabNote.hasComma > 0) {
187
- newTabNote.hasComma--;
188
- } else {
189
- if (!newTabNote.isLower) {
190
- newTabNote.isLower = true;
191
- } else {
192
- newTabNote.isQuoted = true;
193
- }
194
- }
195
- }
196
- return newTabNote;
153
+ var note = midiToNote(this.pitch+1+this.pitchAltered)
154
+ return new TabNote(note)
197
155
  };
198
156
 
199
157
  TabNote.prototype.prevNote = function () {
200
- var newTabNote = cloneNote(this);
201
-
202
- if (this.isSharp) {
203
- newTabNote.isSharp = false;
204
- return newTabNote;
205
- }
206
- var noteIndex = notes.indexOf(this.name);
207
- if (noteIndex == 0) {
208
- noteIndex = notes.length - 1;
209
- } else {
210
- noteIndex--;
211
- }
212
- newTabNote.name = notes[noteIndex];
213
- if (newTabNote.name == 'B') {
214
- if (newTabNote.isLower) {
215
- newTabNote.hasComma = 1;
216
- } else {
217
- if (newTabNote.hasComma > 0) {
218
- newTabNote.hasComma++;
219
- } else {
220
- if (newTabNote.isQuoted > 0) {
221
- newTabNote.isQuoted -= 1;
222
- } else {
223
- newTabNote.isLower = true;
224
- }
225
- }
226
- }
227
- }
228
- if (this.isFlat) {
229
- newTabNote.isFlat = false;
230
- return newTabNote;
231
- } else {
232
- if (this.name != 'E' && this.name != 'B') {
233
- newTabNote.isSharp = true;
234
- }
235
- }
236
- return newTabNote;
158
+ var note = midiToNote(this.pitch-1+this.pitchAltered)
159
+ return new TabNote(note)
237
160
  };
238
161
 
239
162
  TabNote.prototype.emitNoAccidentals = function ( ) {
@@ -3,7 +3,6 @@ var StringTablature = require('../string-tablature');
3
3
  var TabCommon = require('../../tab-common');
4
4
  var TabRenderer = require('../../tab-renderer');
5
5
  var ViolinPatterns = require('./violin-patterns');
6
- var setViolinFonts = require('./violin-fonts');
7
6
 
8
7
 
9
8
  /**
@@ -30,7 +29,6 @@ Plugin.prototype.init = function (abcTune, tuneNumber, params) {
30
29
  Plugin.prototype.render = function (renderer, line, staffIndex) {
31
30
  if (this._super.inError) return;
32
31
  if (this.tablature.bypass(line)) return;
33
- setViolinFonts(this.abcTune);
34
32
  var rndrer = new TabRenderer(this, renderer, line, staffIndex);
35
33
  rndrer.doLayout();
36
34
  };