abcjs-rails 2.0 → 2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (24) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/abcjs/api/abc_animation.js +0 -19
  3. data/app/assets/javascripts/abcjs/api/abc_tunebook.js +0 -5
  4. data/app/assets/javascripts/abcjs/edit/abc_editor.js +0 -10
  5. data/app/assets/javascripts/abcjs/parse/abc_parse.js +11 -35
  6. data/app/assets/javascripts/abcjs/parse/abc_parse_directive.js +2 -18
  7. data/app/assets/javascripts/abcjs/write/abc_absolute_element.js +9 -25
  8. data/app/assets/javascripts/abcjs/write/abc_abstract_engraver.js +21 -16
  9. data/app/assets/javascripts/abcjs/write/abc_beam_element.js +5 -94
  10. data/app/assets/javascripts/abcjs/write/abc_crescendo_element.js +0 -16
  11. data/app/assets/javascripts/abcjs/write/abc_decoration.js +3 -2
  12. data/app/assets/javascripts/abcjs/write/abc_dynamic_decoration.js +0 -8
  13. data/app/assets/javascripts/abcjs/write/abc_ending_element.js +0 -20
  14. data/app/assets/javascripts/abcjs/write/abc_engraver_controller.js +1 -1
  15. data/app/assets/javascripts/abcjs/write/abc_relative_element.js +9 -46
  16. data/app/assets/javascripts/abcjs/write/abc_renderer.js +1 -1
  17. data/app/assets/javascripts/abcjs/write/abc_staff_group_element.js +4 -67
  18. data/app/assets/javascripts/abcjs/write/abc_tie_element.js +0 -24
  19. data/app/assets/javascripts/abcjs/write/abc_triplet_element.js +0 -54
  20. data/app/assets/javascripts/abcjs/write/abc_voice_element.js +24 -13
  21. data/lib/abcjs-rails/version.rb +1 -1
  22. metadata +2 -4
  23. data/app/assets/javascripts/abcjs/write/abc_layout.js +0 -1040
  24. data/app/assets/javascripts/abcjs/write/abc_write.js +0 -535
@@ -457,7 +457,7 @@ ABCJS.write.Renderer.prototype.printStaveLine = function (x1,x2, pitch, klass) {
457
457
 
458
458
  /**
459
459
  * gets scaled if not in a group
460
- * @param {number} x1 x coordinate of the stem
460
+ * @param {number} x x coordinate of the stem
461
461
  * @param {number} dx stem width
462
462
  * @param {number} y1 y coordinate of the stem bottom
463
463
  * @param {number} y2 y coordinate of the stem top
@@ -201,36 +201,21 @@ ABCJS.write.StaffGroupElement.prototype.finished = function() {
201
201
  return true;
202
202
  };
203
203
 
204
- <<<<<<< HEAD
205
204
  ABCJS.write.StaffGroupElement.prototype.layout = function(spacing, renderer, debug) {
205
+ var epsilon = 0.0000001; // Fudging for inexactness of floating point math.
206
206
  this.spacingunits = 0; // number of times we will have ended up using the spacing distance (as opposed to fixed width distances)
207
207
  this.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
208
208
  var x = renderer.padding.left;
209
- =======
210
- ABCJS.write.StaffGroupElement.prototype.layout = function(spacing, controller, debug) {
211
- this.spacingunits = 0; // number of times we will have ended up using the spacing distance (as opposed to fixed width distances)
212
- this.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
213
- var x = controller.paddingleft*controller.scale;
214
- >>>>>>> origin/master
215
209
 
216
210
  // find out how much space will be taken up by voice headers
217
211
  var voiceheaderw = 0;
218
212
  for (var i=0;i<this.voices.length;i++) {
219
213
  if(this.voices[i].header) {
220
- <<<<<<< HEAD
221
214
  var size = renderer.getTextSize(this.voices[i].header, 'voicefont', '');
222
215
  voiceheaderw = Math.max(voiceheaderw,size.width);
223
216
  }
224
217
  }
225
218
  x=x+voiceheaderw*1.1; // When there is no voice header, 110% of 0 is 0
226
- =======
227
- var t = controller.renderText(100, this.y-10, this.voices[i].header, 'voicefont', ''); // code duplicated below // don't scale this as we ask for the bbox
228
- voiceheaderw = Math.max(voiceheaderw,t.getBBox().width);
229
- t.remove();
230
- }
231
- }
232
- x=x+voiceheaderw*(1/controller.scale)*1.1; // 10% of 0 is 0
233
- >>>>>>> origin/master
234
219
  this.startx=x;
235
220
 
236
221
  var currentduration = 0;
@@ -254,7 +239,9 @@ ABCJS.write.StaffGroupElement.prototype.layout = function(spacing, controller, d
254
239
  var currentvoices = [];
255
240
  var othervoices = [];
256
241
  for (i=0;i<this.voices.length;i++) {
257
- if (this.voices[i].getDurationIndex() !== currentduration) {
242
+ var durationIndex = this.voices[i].getDurationIndex();
243
+ // PER: Because of the inexactness of JS floating point math, we just get close.
244
+ if (durationIndex - currentduration > epsilon) {
258
245
  othervoices.push(this.voices[i]);
259
246
  //console.log("out: voice ",i);
260
247
  } else {
@@ -319,7 +306,6 @@ ABCJS.write.StaffGroupElement.prototype.layout = function(spacing, controller, d
319
306
  }
320
307
  };
321
308
 
322
- <<<<<<< HEAD
323
309
  ABCJS.write.StaffGroupElement.prototype.calcHeight = function () {
324
310
  // the height is calculated here in a parallel way to the drawing below in hopes that both of these functions will be modified together.
325
311
  // TODO-PER: also add the space between staves. (That's systemStaffSeparation, which is the minimum distance between the staff LINES.)
@@ -330,33 +316,11 @@ ABCJS.write.StaffGroupElement.prototype.calcHeight = function () {
330
316
  height += staff.top;
331
317
  if (staff.bottom < 0)
332
318
  height += -staff.bottom;
333
- =======
334
- ABCJS.write.StaffGroupElement.prototype.draw = function (renderer, y) {
335
-
336
- this.y = y;
337
- for (var i=0;i<this.staffs.length;i++) {
338
- var shiftabove = this.staffs[i].highest - ((i===0)? 20 : 15);
339
- var shiftbelow = this.staffs[i].lowest - ((i===this.staffs.length-1)? 0 : 0);
340
- this.staffs[i].top = y;
341
- if (shiftabove > 0) y+= shiftabove*ABCJS.write.spacing.STEP;
342
- this.staffs[i].y = y;
343
- y+= ABCJS.write.spacing.STAVEHEIGHT*0.9; // position of the words
344
- if (shiftbelow < 0) y-= shiftbelow*ABCJS.write.spacing.STEP;
345
- this.staffs[i].bottom = y;
346
-
347
- if (this.stafflines[i] !== 0) {
348
- renderer.y = this.staffs[i].y;
349
- // TODO-PER: stafflines should always have been set somewhere, so this shouldn't be necessary.
350
- if (this.stafflines[i] === undefined)
351
- this.stafflines[i] = 5;
352
- renderer.printStave(this.startx, this.w, this.stafflines[i]);
353
- >>>>>>> origin/master
354
319
  }
355
320
  }
356
321
  return height;
357
322
  };
358
323
 
359
- <<<<<<< HEAD
360
324
  ABCJS.write.StaffGroupElement.prototype.draw = function (renderer) {
361
325
  // We enter this method with renderer.y pointing to the topmost coordinate that we're allowed to draw.
362
326
  // All of the children that will be drawn have a relative "pitch" set, where zero is the first ledger line below the staff.
@@ -402,27 +366,10 @@ ABCJS.write.StaffGroupElement.prototype.draw = function (renderer) {
402
366
  }
403
367
  if (staff1.bottom < 0)
404
368
  renderer.moveY(ABCJS.write.spacing.STEP, -staff1.bottom);
405
- =======
406
- var bartop = 0;
407
- renderer.measureNumber = null;
408
- for (i=0;i<this.voices.length;i++) {
409
- this.voices[i].draw(renderer, bartop);
410
- bartop = this.voices[i].barbottom;
411
- }
412
- renderer.measureNumber = null;
413
-
414
- if (this.staffs.length>1) {
415
- renderer.y = this.staffs[0].y;
416
- var top = renderer.calcY(10);
417
- renderer.y = this.staffs[this.staffs.length-1].y;
418
- var bottom = renderer.calcY(2);
419
- renderer.printStem(this.startx, 0.6, top, bottom);
420
- >>>>>>> origin/master
421
369
  }
422
370
  var topLine; // these are to connect multiple staves. We need to remember where they are.
423
371
  var bottomLine;
424
372
 
425
- <<<<<<< HEAD
426
373
  var bartop = 0;
427
374
  renderer.measureNumber = null;
428
375
  for (var i=0;i<this.voices.length;i++) {
@@ -445,16 +392,6 @@ ABCJS.write.StaffGroupElement.prototype.draw = function (renderer) {
445
392
  }
446
393
  }
447
394
  renderer.measureNumber = null;
448
- =======
449
- // for (i=0;i<this.staffs.length;i++) {
450
- // if (this.stafflines[i] === 0) continue;
451
- // renderer.y = this.staffs[i].y;
452
- // // TODO-PER: stafflines should always have been set somewhere, so this shouldn't be necessary.
453
- // if (this.stafflines[i] === undefined)
454
- // this.stafflines[i] = 5;
455
- // renderer.printStave(this.startx,this.w, this.stafflines[i]);
456
- // }
457
- >>>>>>> origin/master
458
395
 
459
396
  // connect all the staves together with a vertical line
460
397
  if (this.staffs.length>1) {
@@ -30,15 +30,9 @@ ABCJS.write.TieElem = function(anchor1, anchor2, above, forceandshift, isTie) {
30
30
  this.isTie = isTie;
31
31
  };
32
32
 
33
- <<<<<<< HEAD
34
33
  ABCJS.write.TieElem.prototype.setEndAnchor = function(anchor2) {
35
34
  this.anchor2 = anchor2; // must have a .x and a .pitch property or be null (means ends at the end of the line)
36
35
  };
37
- =======
38
- ABCJS.write.TieElem.prototype.draw = function (renderer, linestartx, lineendx) {
39
- var startpitch;
40
- var endpitch;
41
- >>>>>>> origin/master
42
36
 
43
37
  // If we encounter a repeat sign, then we don't want to extend either a tie or a slur past it, so these are called to be a limit.
44
38
  ABCJS.write.TieElem.prototype.setStartX = function(startLimitElem) {
@@ -107,24 +101,6 @@ ABCJS.write.TieElem.prototype.layout = function (lineStartX, lineEndX) {
107
101
  ABCJS.write.TieElem.prototype.draw = function (renderer, linestartx, lineendx) {
108
102
  this.layout(linestartx, lineendx);
109
103
 
110
- <<<<<<< HEAD
111
104
  renderer.drawArc(this.startX, this.endX, this.startY, this.endY, this.above);
112
- =======
113
- // if (this.anchor1 && this.anchor2) {
114
- // if ((!this.force && this.anchor1.parent.beam && this.anchor2.parent.beam &&
115
- // this.anchor1.parent.beam.asc===this.anchor2.parent.beam.asc) ||
116
- // ((this.force==="up") || this.force==="down") && this.anchor1.parent.beam && this.anchor2.parent.beam && this.anchor1.parent.beam===this.anchor2.parent.beam) {
117
- // this.above = !this.anchor1.parent.beam.asc;
118
- // preservebeamdir = true;
119
- // }
120
- // }
121
-
122
- // var pitchshift = 0;
123
- // if (this.force==="up" && !preservebeamdir) pitchshift = 7;
124
- // if (this.force==="down" && !preservebeamdir) pitchshift = -7;
125
-
126
- // renderer.debugMsgLow(linestartx, debugMsg);
127
- renderer.drawArc(linestartx, lineendx, startpitch, endpitch, this.above);
128
- >>>>>>> origin/master
129
105
 
130
106
  };
@@ -25,7 +25,6 @@ if (!window.ABCJS.write)
25
25
  (function() {
26
26
  "use strict";
27
27
 
28
- <<<<<<< HEAD
29
28
  ABCJS.write.TripletElem = function(number, anchor1) {
30
29
  this.anchor1 = anchor1; // must have a .x and a .parent property or be null (means starts at the "beginning" of the line - after keysig)
31
30
  this.number = number;
@@ -78,59 +77,11 @@ if (!window.ABCJS.write)
78
77
  y1 = renderer.calcY(y1);
79
78
  y2 = renderer.calcY(y2);
80
79
  var bracketHeight = 5;
81
- =======
82
- ABCJS.write.TripletElem.prototype.draw = function (renderer, linestartx, lineendx) {
83
- // TODO end and beginning of line
84
- if (this.anchor1 && this.anchor2) {
85
- var ypos = this.above?16:-1; // PER: Just bumped this up from 14 to make (3z2B2B2 (3B2B2z2 succeed. There's probably a better way.
86
-
87
- if (this.anchor1.parent.beam &&
88
- this.anchor1.parent.beam===this.anchor2.parent.beam) {
89
- var beam = this.anchor1.parent.beam;
90
- this.above = beam.asc;
91
- ypos = beam.pos;
92
- } else {
93
- this.drawLine(renderer,renderer.calcY(ypos));
94
- }
95
- var xsum = this.anchor1.x+this.anchor2.x;
96
- var ydelta = 0;
97
- if (beam) {
98
- if (this.above) {
99
- xsum += (this.anchor2.w + this.anchor1.w);
100
- ydelta = 4;
101
- } else {
102
- ydelta = -4;
103
- }
104
- } else {
105
- xsum += this.anchor2.w;
106
- }
107
-
108
- renderer.renderText(xsum/2, renderer.calcY(ypos+ydelta), this.number, 'annotationfont', "middle", "triplet"); // TODO-PER: There doesn't seem to be a tripletfont defined.
109
- }
110
- };
111
-
112
- ABCJS.write.TripletElem.prototype.drawLine = function (renderer, y) {
113
- var pathString;
114
- var linestartx = this.anchor1.x;
115
- pathString = ABCJS.write.sprintf("M %f %f L %f %f",
116
- linestartx, y, linestartx, y+5);
117
- renderer.printPath({path:pathString, stroke:"#000000", 'class': renderer.addClasses('triplet')});
118
-
119
- var lineendx = this.anchor2.x+this.anchor2.w;
120
- pathString = ABCJS.write.sprintf("M %f %f L %f %f",
121
- lineendx, y, lineendx, y+5);
122
- renderer.printPath({path:pathString, stroke:"#000000", 'class': renderer.addClasses('triplet')});
123
-
124
- pathString = ABCJS.write.sprintf("M %f %f L %f %f",
125
- linestartx, y, (linestartx+lineendx)/2-5, y);
126
- renderer.printPath({path:pathString, stroke:"#000000", 'class': renderer.addClasses('triplet')});
127
- >>>>>>> origin/master
128
80
 
129
81
  // Draw vertical lines at the beginning and end
130
82
  drawLine(renderer, x1, y1, x1, y1 + bracketHeight);
131
83
  drawLine(renderer, x2, y2, x2, y2 + bracketHeight);
132
84
 
133
- <<<<<<< HEAD
134
85
  // figure out midpoints to draw the broken line.
135
86
  var midX = x1 + (x2-x1)/2;
136
87
  var midY = y1 + (y2-y1)/2;
@@ -144,9 +95,4 @@ ABCJS.write.TripletElem.prototype.drawLine = function (renderer, y) {
144
95
  drawLine(renderer, rightStartX, rightStartY, x2, y2);
145
96
  }
146
97
  })();
147
- =======
148
- pathString = ABCJS.write.sprintf("M %f %f L %f %f",
149
- (linestartx+lineendx)/2+5, y, lineendx, y);
150
- renderer.printPath({path:pathString, stroke:"#000000", 'class': renderer.addClasses('triplet')});
151
- >>>>>>> origin/master
152
98
 
@@ -77,6 +77,29 @@ ABCJS.write.VoiceElement.prototype.setLimit = function(member, child) {
77
77
  this.specialY[member] = Math.max(this.specialY[member], specialY[member]);
78
78
  };
79
79
 
80
+ ABCJS.write.VoiceElement.prototype.moveDecorations = function(beam) {
81
+ var padding = 1.5; // This is the vertical padding between elements, in pitches.
82
+ for (var ch = 0; ch < beam.elems.length; ch++) {
83
+ var child = beam.elems[ch];
84
+ if (child.top) {
85
+ // We now know where the ornaments should have been placed, so move them if they would overlap.
86
+ var top = beam.yAtNote(child);
87
+ for (var i = 0; i < child.children.length; i++) {
88
+ var el = child.children[i];
89
+ if (el.klass === 'ornament') {
90
+ if (el.bottom - padding < top) {
91
+ 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.
92
+ el.bottom += distance;
93
+ el.top += distance;
94
+ el.pitch += distance;
95
+ top = child.top = el.top;
96
+ }
97
+ }
98
+ }
99
+ }
100
+ }
101
+ };
102
+
80
103
  ABCJS.write.VoiceElement.prototype.adjustRange = function(child) {
81
104
  if (child.bottom !== undefined)
82
105
  this.bottom = Math.min(this.bottom, child.bottom);
@@ -201,7 +224,6 @@ ABCJS.write.VoiceElement.prototype.shiftRight = function (dx) {
201
224
 
202
225
  ABCJS.write.VoiceElement.prototype.draw = function (renderer, bartop) {
203
226
  var width = this.w-1;
204
- <<<<<<< HEAD
205
227
  renderer.staffbottom = this.staff.bottom;
206
228
  //this.barbottom = renderer.calcY(2);
207
229
 
@@ -209,18 +231,6 @@ ABCJS.write.VoiceElement.prototype.draw = function (renderer, bartop) {
209
231
  if (this.header) { // print voice name
210
232
  var textpitch = 14 - (this.voicenumber+1)*(12/(this.voicetotal+1));
211
233
  renderer.renderText(renderer.padding.left, renderer.calcY(textpitch), this.header, 'voicefont', 'staff-extra voice-name', 'start');
212
- =======
213
- renderer.y = this.staff.y;
214
- renderer.staffbottom = this.staff.bottom;
215
- this.barbottom = renderer.calcY(2);
216
-
217
- renderer.measureNumber = null;
218
- if (this.header) { // print voice name
219
- var textpitch = 12 - (this.voicenumber+1)*(12/(this.voicetotal+1));
220
- var headerX = (this.startx-renderer.paddingleft)/2+renderer.paddingleft;
221
- headerX = headerX*renderer.scale;
222
- renderer.renderText(headerX, renderer.calcY(textpitch), this.header, 'voicefont', 'staff-extra voice-name');
223
- >>>>>>> origin/master
224
234
  }
225
235
 
226
236
  for (var i=0, ii=this.children.length; i<ii; i++) {
@@ -258,6 +268,7 @@ ABCJS.write.VoiceElement.prototype.layoutBeams = function() {
258
268
  for (var i = 0; i < this.beams.length; i++) {
259
269
  if (this.beams[i].layout) {
260
270
  this.beams[i].layout();
271
+ this.moveDecorations(this.beams[i]);
261
272
  // The above will change the top and bottom of the abselem children, so see if we need to expand our range.
262
273
  for (var j = 0; j < this.beams[i].elems.length; j++) {
263
274
  this.adjustRange(this.beams[i].elems[j]);
@@ -1,5 +1,5 @@
1
1
  module Abcjs
2
2
  module Rails
3
- VERSION = "2.0"
3
+ VERSION = "2.1"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: abcjs-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: '2.0'
4
+ version: '2.1'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Rosen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-19 00:00:00.000000000 Z
11
+ date: 2015-05-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -58,7 +58,6 @@ files:
58
58
  - app/assets/javascripts/abcjs/write/abc_ending_element.js
59
59
  - app/assets/javascripts/abcjs/write/abc_engraver_controller.js
60
60
  - app/assets/javascripts/abcjs/write/abc_glyphs.js
61
- - app/assets/javascripts/abcjs/write/abc_layout.js
62
61
  - app/assets/javascripts/abcjs/write/abc_relative_element.js
63
62
  - app/assets/javascripts/abcjs/write/abc_renderer.js
64
63
  - app/assets/javascripts/abcjs/write/abc_staff_group_element.js
@@ -66,7 +65,6 @@ files:
66
65
  - app/assets/javascripts/abcjs/write/abc_tie_element.js
67
66
  - app/assets/javascripts/abcjs/write/abc_triplet_element.js
68
67
  - app/assets/javascripts/abcjs/write/abc_voice_element.js
69
- - app/assets/javascripts/abcjs/write/abc_write.js
70
68
  - app/assets/javascripts/abcjs/write/sprintf.js
71
69
  - lib/abcjs-rails.rb
72
70
  - lib/abcjs-rails/version.rb
@@ -1,1040 +0,0 @@
1
- // abc_layout.js: Creates a data structure suitable for printing a line of abc
2
- // Copyright (C) 2010 Gregory Dyke (gregdyke at gmail dot com)
3
- //
4
- // This program is free software: you can redistribute it and/or modify
5
- // it under the terms of the GNU General Public License as published by
6
- // the Free Software Foundation, either version 3 of the License, or
7
- // (at your option) any later version.
8
- //
9
- // This program is distributed in the hope that it will be useful,
10
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
11
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
- // GNU General Public License for more details.
13
- //
14
- // You should have received a copy of the GNU General Public License
15
- // along with this program. If not, see <http://www.gnu.org/licenses/>.
16
-
17
- /*global window, ABCJS */
18
-
19
- if (!window.ABCJS)
20
- window.ABCJS = {};
21
-
22
- if (!window.ABCJS.write)
23
- window.ABCJS.write = {};
24
-
25
- ABCJS.write.getDuration = function(elem) {
26
- var d = 0;
27
- if (elem.duration) {
28
- d = elem.duration;
29
- }
30
- return d;
31
- };
32
-
33
- ABCJS.write.getDurlog = function(duration) {
34
- // TODO-PER: This is a hack to prevent a Chrome lockup. Duration should have been defined already,
35
- // but there's definitely a case where it isn't. [Probably something to do with triplets.]
36
- if (duration === undefined) {
37
- return 0;
38
- }
39
- // console.log("getDurlog: " + duration);
40
- return Math.floor(Math.log(duration)/Math.log(2));
41
- };
42
-
43
- ABCJS.write.Layout = function(glyphs, bagpipes) {
44
- this.glyphs = glyphs;
45
- this.isBagpipes = bagpipes;
46
- this.chartable = {rest:{0:"rests.whole", 1:"rests.half", 2:"rests.quarter", 3:"rests.8th", 4: "rests.16th",5: "rests.32nd", 6: "rests.64th", 7: "rests.128th"},
47
- note:{"-1": "noteheads.dbl", 0:"noteheads.whole", 1:"noteheads.half", 2:"noteheads.quarter", 3:"noteheads.quarter", 4:"noteheads.quarter", 5:"noteheads.quarter", 6:"noteheads.quarter"},
48
- uflags:{3:"flags.u8th", 4:"flags.u16th", 5:"flags.u32nd", 6:"flags.u64th"},
49
- dflags:{3:"flags.d8th", 4:"flags.d16th", 5:"flags.d32nd", 6:"flags.d64th"}};
50
- this.slurs = {};
51
- this.ties = [];
52
- this.slursbyvoice = {};
53
- this.tiesbyvoice = {};
54
- this.endingsbyvoice = {};
55
- this.s = 0; // current staff number
56
- this.v = 0; // current voice number on current staff
57
- this.stafflines = 5;
58
- this.tripletmultiplier = 1;
59
- };
60
-
61
- ABCJS.write.Layout.prototype.getCurrentVoiceId = function() {
62
- return "s"+this.s+"v"+this.v;
63
- };
64
-
65
- ABCJS.write.Layout.prototype.pushCrossLineElems = function() {
66
- this.slursbyvoice[this.getCurrentVoiceId()] = this.slurs;
67
- this.tiesbyvoice[this.getCurrentVoiceId()] = this.ties;
68
- this.endingsbyvoice[this.getCurrentVoiceId()] = this.partstartelem;
69
- };
70
-
71
- ABCJS.write.Layout.prototype.popCrossLineElems = function() {
72
- this.slurs = this.slursbyvoice[this.getCurrentVoiceId()] || {};
73
- this.ties = this.tiesbyvoice[this.getCurrentVoiceId()] || [];
74
- this.partstartelem = this.endingsbyvoice[this.getCurrentVoiceId()];
75
- };
76
-
77
- ABCJS.write.Layout.prototype.getElem = function() {
78
- if (this.abcline.length <= this.pos)
79
- return null;
80
- return this.abcline[this.pos];
81
- };
82
-
83
- ABCJS.write.Layout.prototype.getNextElem = function() {
84
- if (this.abcline.length <= this.pos+1)
85
- return null;
86
- return this.abcline[this.pos+1];
87
- };
88
-
89
- ABCJS.write.Layout.prototype.printABCLine = function(staffs) {
90
- this.minY = 2; // PER: This is the lowest that any note reaches. It will be used to set the dynamics row.
91
- this.staffgroup = new ABCJS.write.StaffGroupElement();
92
- for (this.s = 0; this.s < staffs.length; this.s++) {
93
- this.printABCStaff(staffs[this.s]);
94
- }
95
- return this.staffgroup;
96
- };
97
-
98
- function adjustChordVerticalPosition(staffgroup) {
99
- var yPlacement = 16; // no lower than high E
100
- var chordList = [];
101
- for (var i = 0; i < staffgroup.voices.length; i++) {
102
- for (var j = 0; j < staffgroup.voices[i].children.length; j++) {
103
- var absElem = staffgroup.voices[i].children[j];
104
- if (absElem.top+5 > yPlacement)
105
- yPlacement = absElem.top+5;
106
- for (var k = 0; k < absElem.children.length; k++) {
107
- var relElem = absElem.children[k];
108
- if (relElem.type === 'chord')
109
- chordList.push(relElem);
110
- }
111
- }
112
- }
113
- for (i = 0; i < chordList.length; i++) {
114
- var elem = chordList[i];
115
- if (elem.top < yPlacement) {
116
- elem.top = yPlacement;
117
- elem.pitch = yPlacement;
118
- elem.bottom = yPlacement;
119
- if (elem.parent.top < yPlacement)
120
- elem.parent.top = yPlacement;
121
- }
122
- }
123
- }
124
-
125
- ABCJS.write.Layout.prototype.printABCStaff = function(abcstaff) {
126
-
127
- var header = "";
128
- if (abcstaff.bracket) header += "bracket "+abcstaff.bracket+" ";
129
- if (abcstaff.brace) header += "brace "+abcstaff.brace+" ";
130
-
131
-
132
- for (this.v = 0; this.v < abcstaff.voices.length; this.v++) {
133
- this.voice = new ABCJS.write.VoiceElement(this.v,abcstaff.voices.length);
134
- if (this.v===0) {
135
- this.voice.barfrom = (abcstaff.connectBarLines==="start" || abcstaff.connectBarLines==="continue");
136
- this.voice.barto = (abcstaff.connectBarLines==="continue" || abcstaff.connectBarLines==="end");
137
- } else {
138
- this.voice.duplicate = true; // barlines and other duplicate info need not be printed
139
- }
140
- if (abcstaff.title && abcstaff.title[this.v]) this.voice.header=abcstaff.title[this.v];
141
- // TODO make invisible if voice is duplicate
142
- this.voice.addChild(this.printClef(abcstaff.clef));
143
- this.voice.addChild(this.printKeySignature(abcstaff.key));
144
- if (abcstaff.meter) this.voice.addChild(this.printTimeSignature(abcstaff.meter));
145
- this.printABCVoice(abcstaff.voices[this.v]);
146
- this.staffgroup.addVoice(this.voice,this.s,this.stafflines);
147
- }
148
- adjustChordVerticalPosition(this.staffgroup);
149
-
150
- };
151
-
152
- ABCJS.write.Layout.prototype.printABCVoice = function(abcline) {
153
- this.popCrossLineElems();
154
- this.stemdir = (this.isBagpipes)?"down":null;
155
- this.abcline = abcline;
156
- if (this.partstartelem) {
157
- this.partstartelem = new ABCJS.write.EndingElem("", null, null);
158
- this.voice.addOther(this.partstartelem);
159
- }
160
- for (var slur in this.slurs) {
161
- if (this.slurs.hasOwnProperty(slur)) {
162
- this.slurs[slur]= new ABCJS.write.TieElem(null, null, this.slurs[slur].above, this.slurs[slur].force);
163
- this.voice.addOther(this.slurs[slur]);
164
- }
165
- }
166
- for (var i=0; i<this.ties.length; i++) {
167
- this.ties[i]=new ABCJS.write.TieElem(null, null, this.ties[i].above, this.ties[i].force);
168
- this.voice.addOther(this.ties[i]);
169
- }
170
-
171
- for (this.pos=0; this.pos<this.abcline.length; this.pos++) {
172
- var abselems = this.printABCElement();
173
- for (i=0; i<abselems.length; i++) {
174
- this.voice.addChild(abselems[i]);
175
- }
176
- }
177
- this.pushCrossLineElems();
178
- };
179
-
180
-
181
- // return an array of ABCJS.write.AbsoluteElement
182
- ABCJS.write.Layout.prototype.printABCElement = function() {
183
- var elemset = [];
184
- var elem = this.getElem();
185
- switch (elem.el_type) {
186
- case "note":
187
- elemset = this.printBeam();
188
- break;
189
- case "bar":
190
- elemset[0] = this.printBarLine(elem);
191
- if (this.voice.duplicate) elemset[0].invisible = true;
192
- break;
193
- case "meter":
194
- elemset[0] = this.printTimeSignature(elem);
195
- if (this.voice.duplicate) elemset[0].invisible = true;
196
- break;
197
- case "clef":
198
- elemset[0] = this.printClef(elem);
199
- if (this.voice.duplicate) elemset[0].invisible = true;
200
- break;
201
- case "key":
202
- elemset[0] = this.printKeySignature(elem);
203
- if (this.voice.duplicate) elemset[0].invisible = true;
204
- break;
205
- case "stem":
206
- this.stemdir=elem.direction;
207
- break;
208
- case "part":
209
- var abselem = new ABCJS.write.AbsoluteElement(elem,0,0, 'part');
210
- abselem.addChild(new ABCJS.write.RelativeElement(elem.title, 0, 0, 18, {type:"text", attributes:{"font-weight":"bold", "font-size":""+16*this.printer.scale+"px", "font-family":"serif"}}));
211
- elemset[0] = abselem;
212
- break;
213
- // case "tempo":
214
- // this.printer.y = this.printer.printTempo(elem, this.printer.paper, this.printer.layouter, this.printer.y, this.printer, this.printer.x);
215
- // break;
216
- default:
217
- var abselem2 = new ABCJS.write.AbsoluteElement(elem,0,0, 'unsupported');
218
- abselem2.addChild(new ABCJS.write.RelativeElement("element type "+elem.el_type, 0, 0, 0, {type:"debug"}));
219
- elemset[0] = abselem2;
220
- }
221
-
222
- return elemset;
223
- };
224
-
225
- ABCJS.write.Layout.prototype.printBeam = function() {
226
- var abselemset = [];
227
-
228
- if (this.getElem().startBeam && !this.getElem().endBeam) {
229
- var beamelem = new ABCJS.write.BeamElem(this.stemdir);
230
- // PER: need two passes: the first one decides if the stems are up or down.
231
- // TODO-PER: This could be more efficient.
232
- var oldPos = this.pos;
233
- var abselem;
234
- while (this.getElem()) {
235
- abselem = this.printNote(this.getElem(),true,true);
236
- beamelem.add(abselem);
237
- if (this.getElem().endBeam)
238
- break;
239
- this.pos++;
240
- }
241
- var dir = beamelem.calcDir();
242
- this.pos = oldPos;
243
-
244
- beamelem = new ABCJS.write.BeamElem(dir ? "up" : "down");
245
- var oldDir = this.stemdir;
246
- this.stemdir = dir ? "up" : "down";
247
- while (this.getElem()) {
248
- abselem = this.printNote(this.getElem(),true);
249
- abselemset.push(abselem);
250
- beamelem.add(abselem);
251
- if (this.getElem().endBeam) {
252
- break;
253
- }
254
- this.pos++;
255
- }
256
- this.stemdir = oldDir;
257
- this.voice.addOther(beamelem);
258
- } else {
259
- abselemset[0] = this.printNote(this.getElem());
260
- }
261
- return abselemset;
262
- };
263
-
264
- ABCJS.write.sortPitch = function(elem) {
265
- var sorted;
266
- do {
267
- sorted = true;
268
- for (var p = 0; p<elem.pitches.length-1; p++) {
269
- if (elem.pitches[p].pitch>elem.pitches[p+1].pitch) {
270
- sorted = false;
271
- var tmp = elem.pitches[p];
272
- elem.pitches[p] = elem.pitches[p+1];
273
- elem.pitches[p+1] = tmp;
274
- }
275
- }
276
- } while (!sorted);
277
- };
278
-
279
- ABCJS.write.Layout.prototype.printNote = function(elem, nostem, dontDraw) { //stem presence: true for drawing stemless notehead
280
- var notehead = null;
281
- var grace= null;
282
- this.roomtaken = 0; // room needed to the left of the note
283
- this.roomtakenright = 0; // room needed to the right of the note
284
- var dotshiftx = 0; // room taken by chords with displaced noteheads which cause dots to shift
285
- var c="";
286
- var flag = null;
287
- var additionalLedgers = []; // PER: handle the case of [bc'], where the b doesn't have a ledger line
288
-
289
- var p, i, pp;
290
- var width, p1, p2, dx;
291
-
292
- var duration = ABCJS.write.getDuration(elem);
293
- if (duration === 0) { duration = 0.25; nostem = true; } //PER: zero duration will draw a quarter note head.
294
- var durlog = Math.floor(Math.log(duration)/Math.log(2)); //TODO use getDurlog
295
- var dot=0;
296
-
297
- for (var tot = Math.pow(2,durlog), inc=tot/2; tot<duration; dot++,tot+=inc,inc/=2);
298
-
299
-
300
- if (elem.startTriplet) {
301
- if (elem.startTriplet === 2)
302
- this.tripletmultiplier = 3/2;
303
- else
304
- this.tripletmultiplier=(elem.startTriplet-1)/elem.startTriplet;
305
- }
306
-
307
-
308
- var abselem = new ABCJS.write.AbsoluteElement(elem, duration * this.tripletmultiplier, 1, 'note');
309
-
310
-
311
- if (elem.rest) {
312
- var restpitch = 7;
313
- if (this.stemdir==="down") restpitch = 3;
314
- if (this.stemdir==="up") restpitch = 11;
315
- switch(elem.rest.type) {
316
- case "whole":
317
- c = this.chartable.rest[0];
318
- elem.averagepitch=restpitch;
319
- elem.minpitch=restpitch;
320
- elem.maxpitch=restpitch;
321
- dot = 0;
322
- break;
323
- case "rest":
324
- c = this.chartable.rest[-durlog];
325
- elem.averagepitch=restpitch;
326
- elem.minpitch=restpitch;
327
- elem.maxpitch=restpitch;
328
- break;
329
- case "invisible":
330
- case "spacer":
331
- c="";
332
- }
333
- if (!dontDraw)
334
- notehead = this.printNoteHead(abselem, c, {verticalPos:restpitch}, null, 0, -this.roomtaken, null, dot, 0, 1);
335
- if (notehead) abselem.addHead(notehead);
336
- this.roomtaken+=this.accidentalshiftx;
337
- this.roomtakenright = Math.max(this.roomtakenright,this.dotshiftx);
338
-
339
- } else {
340
- ABCJS.write.sortPitch(elem);
341
-
342
- // determine averagepitch, minpitch, maxpitch and stem direction
343
- var sum=0;
344
- for (p=0, pp=elem.pitches.length; p<pp; p++) {
345
- sum += elem.pitches[p].verticalPos;
346
- }
347
- elem.averagepitch = sum/elem.pitches.length;
348
- elem.minpitch = elem.pitches[0].verticalPos;
349
- this.minY = Math.min(elem.minpitch, this.minY);
350
- elem.maxpitch = elem.pitches[elem.pitches.length-1].verticalPos;
351
- var dir = (elem.averagepitch>=6) ? "down": "up";
352
- if (this.stemdir) dir=this.stemdir;
353
-
354
- // determine elements of chords which should be shifted
355
- for (p=(dir==="down")?elem.pitches.length-2:1; (dir==="down")?p>=0:p<elem.pitches.length; p=(dir==="down")?p-1:p+1) {
356
- var prev = elem.pitches[(dir==="down")?p+1:p-1];
357
- var curr = elem.pitches[p];
358
- var delta = (dir==="down")?prev.pitch-curr.pitch:curr.pitch-prev.pitch;
359
- if (delta<=1 && !prev.printer_shift) {
360
- curr.printer_shift=(delta)?"different":"same";
361
- if (curr.verticalPos > 11 || curr.verticalPos < 1) { // PER: add extra ledger line
362
- additionalLedgers.push(curr.verticalPos - (curr.verticalPos%2));
363
- }
364
- if (dir==="down") {
365
- this.roomtaken = this.glyphs.getSymbolWidth(this.chartable.note[-durlog])+2;
366
- } else {
367
- dotshiftx = this.glyphs.getSymbolWidth(this.chartable.note[-durlog])+2;
368
- }
369
- }
370
- }
371
-
372
- // The accidentalSlot will hold a list of all the accidentals on this chord. Each element is a vertical place,
373
- // and contains a pitch, which is the last pitch that contains an accidental in that slot. The slots are numbered
374
- // from closest to the note to farther left. We only need to know the last accidental we placed because
375
- // we know that the pitches are sorted by now.
376
- this.accidentalSlot = [];
377
-
378
- for (p=0; p<elem.pitches.length; p++) {
379
-
380
- if (!nostem) {
381
- if ((dir==="down" && p!==0) || (dir==="up" && p!==pp-1)) { // not the stemmed elem of the chord
382
- flag = null;
383
- } else {
384
- flag = this.chartable[(dir==="down")?"dflags":"uflags"][-durlog];
385
- }
386
- c = this.chartable.note[-durlog];
387
- } else {
388
- c="noteheads.quarter";
389
- }
390
-
391
- // The highest position for the sake of placing slurs is itself if the slur is internal. It is the highest position possible if the slur is for the whole chord.
392
- // If the note is the only one in the chord, then any slur it has counts as if it were on the whole chord.
393
- elem.pitches[p].highestVert = elem.pitches[p].verticalPos;
394
- var isTopWhenStemIsDown = (this.stemdir==="up" || dir==="up") && p===0;
395
- var isBottomWhenStemIsUp = (this.stemdir==="down" || dir==="down") && p===pp-1;
396
- if (!dontDraw && (isTopWhenStemIsDown || isBottomWhenStemIsUp)) { // place to put slurs if not already on pitches
397
-
398
- if (elem.startSlur || pp === 1) {
399
- elem.pitches[p].highestVert = elem.pitches[pp-1].verticalPos;
400
- if (this.stemdir==="up" || dir==="up")
401
- elem.pitches[p].highestVert += 6; // If the stem is up, then compensate for the length of the stem
402
- }
403
- if (elem.startSlur) {
404
- if (!elem.pitches[p].startSlur) elem.pitches[p].startSlur = []; //TODO possibly redundant, provided array is not optional
405
- for (i=0; i<elem.startSlur.length; i++) {
406
- elem.pitches[p].startSlur.push(elem.startSlur[i]);
407
- }
408
- }
409
-
410
- if (!dontDraw && elem.endSlur) {
411
- elem.pitches[p].highestVert = elem.pitches[pp-1].verticalPos;
412
- if (this.stemdir==="up" || dir==="up")
413
- elem.pitches[p].highestVert += 6; // If the stem is up, then compensate for the length of the stem
414
- if (!elem.pitches[p].endSlur) elem.pitches[p].endSlur = []; //TODO possibly redundant, provided array is not optional
415
- for (i=0; i<elem.endSlur.length; i++) {
416
- elem.pitches[p].endSlur.push(elem.endSlur[i]);
417
- }
418
- }
419
- }
420
-
421
- if (!dontDraw)
422
- notehead = this.printNoteHead(abselem, c, elem.pitches[p], dir, 0, -this.roomtaken, flag, dot, dotshiftx, 1);
423
- if (notehead) abselem.addHead(notehead);
424
- this.roomtaken += this.accidentalshiftx;
425
- this.roomtakenright = Math.max(this.roomtakenright,this.dotshiftx);
426
- }
427
-
428
- // draw stem from the furthest note to a pitch above/below the stemmed note
429
- if (!nostem && durlog<=-1) {
430
- p1 = (dir==="down") ? elem.minpitch-7 : elem.minpitch+1/3;
431
- // PER added stemdir test to make the line meet the note.
432
- if (p1>6 && !this.stemdir) p1=6;
433
- p2 = (dir==="down") ? elem.maxpitch-1/3 : elem.maxpitch+7;
434
- // PER added stemdir test to make the line meet the note.
435
- if (p2<6 && !this.stemdir) p2=6;
436
- dx = (dir==="down" || abselem.heads.length === 0)?0:abselem.heads[0].w;
437
- width = (dir==="down")?1:-1;
438
- abselem.addExtra(new ABCJS.write.RelativeElement(null, dx, 0, p1, {"type": "stem", "pitch2":p2, linewidth: width}));
439
- this.minY = Math.min(p1, this.minY);
440
- this.minY = Math.min(p2, this.minY);
441
- }
442
-
443
- }
444
-
445
- if (elem.lyric !== undefined) {
446
- var lyricStr = "";
447
- window.ABCJS.parse.each(elem.lyric, function(ly) {
448
- lyricStr += ly.syllable + ly.divider + "\n";
449
- });
450
- abselem.addRight(new ABCJS.write.RelativeElement(lyricStr, 0, lyricStr.length*5, 0, {type:"lyric"}));
451
- }
452
-
453
- if (!dontDraw && elem.gracenotes !== undefined) {
454
- var gracescale = 3/5;
455
- var gracebeam = null;
456
- if (elem.gracenotes.length>1) {
457
- gracebeam = new ABCJS.write.BeamElem("grace",this.isBagpipes);
458
- }
459
-
460
- var graceoffsets = [];
461
- for (i=elem.gracenotes.length-1; i>=0; i--) { // figure out where to place each gracenote
462
- this.roomtaken+=10;
463
- graceoffsets[i] = this.roomtaken;
464
- if (elem.gracenotes[i].accidental) {
465
- this.roomtaken+=7;
466
- }
467
- }
468
-
469
- for (i=0; i<elem.gracenotes.length; i++) {
470
- var gracepitch = elem.gracenotes[i].verticalPos;
471
-
472
- flag = (gracebeam) ? null : this.chartable.uflags[(this.isBagpipes)?5:3];
473
- grace = this.printNoteHead(abselem, "noteheads.quarter", elem.gracenotes[i], "up", -graceoffsets[i], -graceoffsets[i], flag, 0, 0, gracescale);
474
- abselem.addExtra(grace);
475
- // PER: added acciaccatura slash
476
- if (elem.gracenotes[i].acciaccatura) {
477
- var pos = elem.gracenotes[i].verticalPos+7*gracescale; // the same formula that determines the flag position.
478
- var dAcciaccatura = gracebeam ? 5 : 6; // just an offset to make it line up correctly.
479
- abselem.addRight(new ABCJS.write.RelativeElement("flags.ugrace", -graceoffsets[i]+dAcciaccatura, 0, pos, {scalex:gracescale, scaley: gracescale}));
480
- }
481
- if (gracebeam) { // give the beam the necessary info
482
- var pseudoabselem = {heads:[grace],
483
- abcelem:{averagepitch: gracepitch, minpitch: gracepitch, maxpitch: gracepitch},
484
- duration:(this.isBagpipes)?1/32:1/16};
485
- gracebeam.add(pseudoabselem);
486
- } else { // draw the stem
487
- p1 = gracepitch+1/3*gracescale;
488
- p2 = gracepitch+7*gracescale;
489
- dx = grace.dx + grace.w;
490
- width = -0.6;
491
- abselem.addExtra(new ABCJS.write.RelativeElement(null, dx, 0, p1, {"type": "stem", "pitch2":p2, linewidth: width}));
492
- }
493
-
494
- if (i===0 && !this.isBagpipes && !(elem.rest && (elem.rest.type==="spacer"||elem.rest.type==="invisible"))) this.voice.addOther(new ABCJS.write.TieElem(grace, notehead, false, true));
495
- }
496
-
497
- if (gracebeam) {
498
- this.voice.addOther(gracebeam);
499
- }
500
- }
501
-
502
- if (!dontDraw && elem.decoration) {
503
- var addMark = this.printDecoration(elem.decoration, elem.maxpitch, (notehead)?notehead.w:0, abselem, this.roomtaken, dir, elem.minpitch);
504
- if (addMark) {
505
- abselem.klass = "mark";
506
- }
507
- }
508
-
509
- if (elem.barNumber) {
510
- abselem.addChild(new ABCJS.write.RelativeElement(elem.barNumber, -15, 0, 13, {type:"barNumber"}));
511
- }
512
-
513
- // ledger lines
514
- for (i=elem.maxpitch; i>11; i--) {
515
- if (i%2===0 && !elem.rest) {
516
- abselem.addChild(new ABCJS.write.RelativeElement(null, -2, this.glyphs.getSymbolWidth(c)+4, i, {type:"ledger"}));
517
- }
518
- }
519
-
520
- for (i=elem.minpitch; i<1; i++) {
521
- if (i%2===0 && !elem.rest) {
522
- abselem.addChild(new ABCJS.write.RelativeElement(null, -2, this.glyphs.getSymbolWidth(c)+4, i, {type:"ledger"}));
523
- }
524
- }
525
-
526
- for (i = 0; i < additionalLedgers.length; i++) { // PER: draw additional ledgers
527
- var ofs = this.glyphs.getSymbolWidth(c);
528
- if (dir === 'down') ofs = -ofs;
529
- abselem.addChild(new ABCJS.write.RelativeElement(null, ofs-2, this.glyphs.getSymbolWidth(c)+4, additionalLedgers[i], {type:"ledger"}));
530
- }
531
-
532
- if (elem.chord !== undefined) {
533
- for (i = 0; i < elem.chord.length; i++) {
534
- var x = 0;
535
- var y;
536
- switch (elem.chord[i].position) {
537
- case "left":
538
- this.roomtaken+=7;
539
- x = -this.roomtaken; // TODO-PER: This is just a guess from trial and error
540
- y = elem.averagepitch;
541
- abselem.addExtra(new ABCJS.write.RelativeElement(elem.chord[i].name, x, this.glyphs.getSymbolWidth(elem.chord[i].name[0])+4, y, {type:"text"}));
542
- break;
543
- case "right":
544
- this.roomtakenright+=4;
545
- x = this.roomtakenright;// TODO-PER: This is just a guess from trial and error
546
- y = elem.averagepitch;
547
- abselem.addRight(new ABCJS.write.RelativeElement(elem.chord[i].name, x, this.glyphs.getSymbolWidth(elem.chord[i].name[0])+4, y, {type:"text"}));
548
- break;
549
- case "below":
550
- y = elem.minpitch-4;
551
- if (y > -3) y = -3;
552
- var eachLine = elem.chord[i].name.split("\n");
553
- for (var ii = 0; ii < eachLine.length; ii++) {
554
- abselem.addChild(new ABCJS.write.RelativeElement(eachLine[ii], x, 0, y, {type:"text"}));
555
- y -= 3; // TODO-PER: This should actually be based on the font height.
556
- }
557
- break;
558
- default:
559
- if (elem.chord[i].rel_position)
560
- abselem.addChild(new ABCJS.write.RelativeElement(elem.chord[i].name, x+elem.chord[i].rel_position.x, 0, elem.minpitch+elem.chord[i].rel_position.y/ABCJS.write.spacing.STEP, {type:"text"}));
561
- else {
562
- // setting the y-coordinate to zero for now: it will be overwritten later one, after we figure out what the highest element on the line is.
563
- abselem.addChild(new ABCJS.write.RelativeElement(elem.chord[i].name, x, 0, 0, {type: "chord"}));
564
- }
565
- }
566
- }
567
- }
568
-
569
-
570
- if (elem.startTriplet) {
571
- this.triplet = new ABCJS.write.TripletElem(elem.startTriplet, notehead, null, true); // above is opposite from case of slurs
572
- if (!dontDraw)
573
- this.voice.addOther(this.triplet);
574
- }
575
-
576
- if (elem.endTriplet && this.triplet) {
577
- this.triplet.anchor2 = notehead;
578
- this.triplet = null;
579
- this.tripletmultiplier = 1;
580
- }
581
-
582
- return abselem;
583
- };
584
-
585
-
586
-
587
-
588
- ABCJS.write.Layout.prototype.printNoteHead = function(abselem, c, pitchelem, dir, headx, extrax, flag, dot, dotshiftx, scale) {
589
-
590
- // TODO scale the dot as well
591
- var pitch = pitchelem.verticalPos;
592
- var notehead;
593
- var i;
594
- this.accidentalshiftx = 0;
595
- this.dotshiftx = 0;
596
- if (c === undefined)
597
- abselem.addChild(new ABCJS.write.RelativeElement("pitch is undefined", 0, 0, 0, {type:"debug"}));
598
- else if (c==="") {
599
- notehead = new ABCJS.write.RelativeElement(null, 0, 0, pitch);
600
- } else {
601
- var shiftheadx = headx;
602
- if (pitchelem.printer_shift) {
603
- var adjust = (pitchelem.printer_shift==="same")?1:0;
604
- shiftheadx = (dir==="down")?-this.glyphs.getSymbolWidth(c)*scale+adjust:this.glyphs.getSymbolWidth(c)*scale-adjust;
605
- }
606
- notehead = new ABCJS.write.RelativeElement(c, shiftheadx, this.glyphs.getSymbolWidth(c)*scale, pitch, {scalex:scale, scaley: scale, extreme: ((dir==="down")?"below":"above")});
607
- if (flag) {
608
- var pos = pitch+((dir==="down")?-7:7)*scale;
609
- if (scale===1 && (dir==="down")?(pos>6):(pos<6)) pos=6;
610
- var xdelta = (dir==="down")?headx:headx+notehead.w-0.6;
611
- abselem.addRight(new ABCJS.write.RelativeElement(flag, xdelta, this.glyphs.getSymbolWidth(flag)*scale, pos, {scalex:scale, scaley: scale}));
612
- }
613
- this.dotshiftx = notehead.w+dotshiftx-2+5*dot;
614
- for (;dot>0;dot--) {
615
- var dotadjusty = (1-Math.abs(pitch)%2); //PER: take abs value of the pitch. And the shift still happens on ledger lines.
616
- abselem.addRight(new ABCJS.write.RelativeElement("dots.dot", notehead.w+dotshiftx-2+5*dot, this.glyphs.getSymbolWidth("dots.dot"), pitch+dotadjusty));
617
- }
618
- }
619
- if (notehead)
620
- notehead.highestVert = pitchelem.highestVert;
621
-
622
- if (pitchelem.accidental) {
623
- var symb;
624
- switch (pitchelem.accidental) {
625
- case "quartersharp":
626
- symb = "accidentals.halfsharp";
627
- break;
628
- case "dblsharp":
629
- symb = "accidentals.dblsharp";
630
- break;
631
- case "sharp":
632
- symb = "accidentals.sharp";
633
- break;
634
- case "quarterflat":
635
- symb = "accidentals.halfflat";
636
- break;
637
- case "flat":
638
- symb = "accidentals.flat";
639
- break;
640
- case "dblflat":
641
- symb = "accidentals.dblflat";
642
- break;
643
- case "natural":
644
- symb = "accidentals.nat";
645
- }
646
- // if a note is at least a sixth away, it can share a slot with another accidental
647
- var accSlotFound = false;
648
- var accPlace = extrax;
649
- for (var j = 0; j < this.accidentalSlot.length; j++) {
650
- if (pitch - this.accidentalSlot[j][0] >= 6) {
651
- this.accidentalSlot[j][0] = pitch;
652
- accPlace = this.accidentalSlot[j][1];
653
- accSlotFound = true;
654
- break;
655
- }
656
- }
657
- if (accSlotFound === false) {
658
- accPlace -= (this.glyphs.getSymbolWidth(symb)*scale+2);
659
- this.accidentalSlot.push([pitch,accPlace]);
660
- this.accidentalshiftx = (this.glyphs.getSymbolWidth(symb)*scale+2);
661
- }
662
- abselem.addExtra(new ABCJS.write.RelativeElement(symb, accPlace, this.glyphs.getSymbolWidth(symb), pitch, {scalex:scale, scaley: scale}));
663
- }
664
-
665
- if (pitchelem.endTie) {
666
- if (this.ties[0]) {
667
- this.ties[0].anchor2=notehead;
668
- this.ties = this.ties.slice(1,this.ties.length);
669
- }
670
- }
671
-
672
- if (pitchelem.startTie) {
673
- //PER: bug fix: var tie = new ABCJS.write.TieElem(notehead, null, (this.stemdir=="up" || dir=="down") && this.stemdir!="down",(this.stemdir=="down" || this.stemdir=="up"));
674
- var tie = new ABCJS.write.TieElem(notehead, null, (this.stemdir==="down" || dir==="down") && this.stemdir!=="up",(this.stemdir==="down" || this.stemdir==="up"));
675
- this.ties[this.ties.length]=tie;
676
- this.voice.addOther(tie);
677
- // HACK-PER: For the animation, we need to know if a note is tied to the next one, so here's a flag.
678
- // Unfortunately, only some of the notes in the current event might be tied, but this will consider it
679
- // tied if any one of them is. That will work for most cases.
680
- abselem.startTie = true;
681
- }
682
-
683
- if (pitchelem.endSlur) {
684
- for (i=0; i<pitchelem.endSlur.length; i++) {
685
- var slurid = pitchelem.endSlur[i];
686
- var slur;
687
- if (this.slurs[slurid]) {
688
- slur = this.slurs[slurid].anchor2=notehead;
689
- delete this.slurs[slurid];
690
- } else {
691
- slur = new ABCJS.write.TieElem(null, notehead, dir==="down",(this.stemdir==="up" || dir==="down") && this.stemdir!=="down", this.stemdir);
692
- this.voice.addOther(slur);
693
- }
694
- if (this.startlimitelem) {
695
- slur.startlimitelem = this.startlimitelem;
696
- }
697
- }
698
- }
699
-
700
- if (pitchelem.startSlur) {
701
- for (i=0; i<pitchelem.startSlur.length; i++) {
702
- var slurid = pitchelem.startSlur[i].label;
703
- //PER: bug fix: var slur = new ABCJS.write.TieElem(notehead, null, (this.stemdir=="up" || dir=="down") && this.stemdir!="down", this.stemdir);
704
- var slur = new ABCJS.write.TieElem(notehead, null, (this.stemdir==="down" || dir==="down") && this.stemdir!=="up", false);
705
- this.slurs[slurid]=slur;
706
- this.voice.addOther(slur);
707
- }
708
- }
709
-
710
- return notehead;
711
-
712
- };
713
-
714
- ABCJS.write.Layout.prototype.printDecoration = function(decoration, pitch, width, abselem, roomtaken, dir, minPitch) {
715
- var dec;
716
- var compoundDec; // PER: for decorations with two symbols
717
- var diminuendo;
718
- var crescendo;
719
- var unknowndecs = [];
720
- var yslot = (pitch>9) ? pitch+3 : 12;
721
- var ypos;
722
- //var dir = (this.stemdir==="down" || pitch>=6) && this.stemdir!=="up";
723
- var below = false; // PER: whether decoration goes above or below.
724
- var yslotB = this.minY - 4; // (pitch<1) ? pitch-9 : -6;
725
- var i;
726
- roomtaken = roomtaken || 0;
727
- if (pitch===5) yslot=14; // avoid upstem of the A
728
- var addMark = false; // PER: to allow the user to add a class whereever
729
-
730
- for (i=0;i<decoration.length; i++) { // treat staccato, accent, and tenuto first (may need to shift other markers)
731
- if (decoration[i]==="staccato" || decoration[i]==="tenuto" || decoration[i] === "accent") {
732
- var symbol = "scripts." + decoration[i];
733
- if (decoration[i] === "accent") symbol = "scripts.sforzato";
734
- if (ypos === undefined)
735
- ypos = (dir==="down") ? pitch+2:minPitch-2;
736
- else
737
- ypos = (dir==="down") ? ypos+2:ypos-2;
738
- if (decoration[i] === "accent") {
739
- // Always place the accent three pitches away, no matter whether that is a line or space.
740
- if (dir === "up") ypos--;
741
- else ypos++;
742
- } else {
743
- // don't place on a stave line. The stave lines are 2,4,6,8,10
744
- switch (ypos) {
745
- case 2:
746
- case 4:
747
- case 6:
748
- case 8:
749
- case 10:
750
- if (dir === "up") ypos--;
751
- else ypos++;
752
- break;
753
- }
754
- }
755
- if (pitch>9) yslot++; // take up some room of those that are above
756
- var deltax = width/2;
757
- if (this.glyphs.getSymbolAlign(symbol)!=="center") {
758
- deltax -= (this.glyphs.getSymbolWidth(dec)/2);
759
- }
760
- abselem.addChild(new ABCJS.write.RelativeElement(symbol, deltax, this.glyphs.getSymbolWidth(symbol), ypos));
761
- }
762
- if (decoration[i]==="slide" && abselem.heads[0]) {
763
- var ypos2 = abselem.heads[0].pitch;
764
- var blank1 = new ABCJS.write.RelativeElement("", -roomtaken-15, 0, ypos2-1);
765
- var blank2 = new ABCJS.write.RelativeElement("", -roomtaken-5, 0, ypos2+1);
766
- abselem.addChild(blank1);
767
- abselem.addChild(blank2);
768
- this.voice.addOther(new ABCJS.write.TieElem(blank1, blank2, false));
769
- }
770
- }
771
- // If ypos is set at this point it means that there were "close" decorations. Those might be encroaching on where the rest of the decorations go, so they need to be bumped up.
772
- if (ypos+2 > yslot) yslot = ypos+2;
773
-
774
- for (i=0;i<decoration.length; i++) {
775
- below = false;
776
- switch(decoration[i]) {
777
- case "trill":dec="scripts.trill";break;
778
- case "roll": dec="scripts.roll"; break; //TODO put abc2ps roll in here
779
- case "irishroll": dec="scripts.roll"; break;
780
- case "marcato": dec="scripts.umarcato"; break;
781
- case "marcato2": dec="scriopts.dmarcato"; break;//other marcato
782
- case "turn": dec="scripts.turn"; break;
783
- case "uppermordent": dec="scripts.prall"; break;
784
- case "pralltriller": dec="scripts.prall"; break;
785
- case "mordent":
786
- case "lowermordent": dec="scripts.mordent"; break;
787
- case "staccato":
788
- case "accent":
789
- case "tenuto":
790
- case "slide": continue;
791
- case "downbow": dec="scripts.downbow";break;
792
- case "upbow": dec="scripts.upbow";break;
793
- case "fermata": dec="scripts.ufermata"; break;
794
- case "invertedfermata": below = true; dec="scripts.dfermata"; break;
795
- case "breath": dec=","; break;
796
- // case "accent": dec="scripts.sforzato"; break;
797
- case "umarcato": dec="scripts.umarcato"; break;
798
- case "coda": dec="scripts.coda"; break;
799
- case "segno": dec="scripts.segno"; break;
800
- case "/": compoundDec=["flags.ugrace", 1]; continue; // PER: added new decorations
801
- case "//": compoundDec=["flags.ugrace", 2]; continue;
802
- case "///": compoundDec=["flags.ugrace", 3]; continue;
803
- case "////": compoundDec=["flags.ugrace", 4]; continue;
804
- case "p":
805
- case "mp":
806
- case "pp":
807
- case "ppp":
808
- case "pppp":
809
- case "f":
810
- case "ff":
811
- case "fff":
812
- case "ffff":
813
- case "sfz":
814
- case "mf":
815
- var ddelem = new ABCJS.write.DynamicDecoration(abselem, decoration[i]);
816
- this.voice.addOther(ddelem);
817
- continue;
818
- case "mark": addMark = true; continue;
819
- case "diminuendo(":
820
- ABCJS.write.Layout.prototype.startDiminuendoX = abselem;
821
- diminuendo = undefined;
822
- continue;
823
- case "diminuendo)":
824
- diminuendo = { start: ABCJS.write.Layout.prototype.startDiminuendoX, stop: abselem};
825
- ABCJS.write.Layout.prototype.startDiminuendoX = undefined;
826
- continue;
827
- case "crescendo(":
828
- ABCJS.write.Layout.prototype.startCrescendoX = abselem;
829
- crescendo = undefined;
830
- continue;
831
- case "crescendo)":
832
- crescendo = { start: ABCJS.write.Layout.prototype.startCrescendoX, stop: abselem};
833
- ABCJS.write.Layout.prototype.startCrescendoX = undefined;
834
- continue;
835
- default:
836
- unknowndecs[unknowndecs.length]=decoration[i];
837
- continue;
838
- }
839
- if (below) {
840
- ypos = yslotB;
841
- yslotB -= 5;
842
- } else {
843
- ypos=yslot;
844
- yslot+=5;
845
- }
846
- var deltax = width/2;
847
- if (this.glyphs.getSymbolAlign(dec)!=="center") {
848
- deltax -= (this.glyphs.getSymbolWidth(dec)/2);
849
- }
850
- abselem.addChild(new ABCJS.write.RelativeElement(dec, deltax, this.glyphs.getSymbolWidth(dec), ypos));
851
- }
852
- if (compoundDec) { // PER: added new decorations
853
- ypos = (dir === 'down') ? pitch+1:pitch+9;
854
- deltax = width/2;
855
- deltax += (dir === 'down') ? -5 : 3;
856
- for (var xx = 0; xx < compoundDec[1]; xx++) {
857
- ypos -= 1;
858
- abselem.addChild(new ABCJS.write.RelativeElement(compoundDec[0], deltax, this.glyphs.getSymbolWidth(compoundDec[0]), ypos));
859
- }
860
- }
861
- if (diminuendo) {
862
- var delem = new ABCJS.write.CrescendoElem(diminuendo.start, diminuendo.stop, ">");
863
- this.voice.addOther(delem);
864
- }
865
- if (crescendo) {
866
- var celem = new ABCJS.write.CrescendoElem(crescendo.start, crescendo.stop, "<");
867
- this.voice.addOther(celem);
868
- }
869
- if (unknowndecs.length>0)
870
- abselem.addChild(new ABCJS.write.RelativeElement(unknowndecs.join(','), 0, 0, 0, {type:"debug"}));
871
- return addMark;
872
- };
873
-
874
- ABCJS.write.Layout.prototype.printBarLine = function (elem) {
875
- // bar_thin, bar_thin_thick, bar_thin_thin, bar_thick_thin, bar_right_repeat, bar_left_repeat, bar_double_repeat
876
-
877
- var abselem = new ABCJS.write.AbsoluteElement(elem, 0, 10, 'bar');
878
- var anchor = null; // place to attach part lines
879
- var dx = 0;
880
-
881
-
882
-
883
- var firstdots = (elem.type==="bar_right_repeat" || elem.type==="bar_dbl_repeat");
884
- var firstthin = (elem.type!=="bar_left_repeat" && elem.type!=="bar_thick_thin" && elem.type!=="bar_invisible");
885
- var thick = (elem.type==="bar_right_repeat" || elem.type==="bar_dbl_repeat" || elem.type==="bar_left_repeat" ||
886
- elem.type==="bar_thin_thick" || elem.type==="bar_thick_thin");
887
- var secondthin = (elem.type==="bar_left_repeat" || elem.type==="bar_thick_thin" || elem.type==="bar_thin_thin" || elem.type==="bar_dbl_repeat");
888
- var seconddots = (elem.type==="bar_left_repeat" || elem.type==="bar_dbl_repeat");
889
-
890
- // limit positioning of slurs
891
- if (firstdots || seconddots) {
892
- for (var slur in this.slurs) {
893
- if (this.slurs.hasOwnProperty(slur)) {
894
- this.slurs[slur].endlimitelem = abselem;
895
- }
896
- }
897
- this.startlimitelem = abselem;
898
- }
899
-
900
- if (firstdots) {
901
- abselem.addRight(new ABCJS.write.RelativeElement("dots.dot", dx, 1, 7));
902
- abselem.addRight(new ABCJS.write.RelativeElement("dots.dot", dx, 1, 5));
903
- dx+=6; //2 hardcoded, twice;
904
- }
905
-
906
- if (firstthin) {
907
- anchor = new ABCJS.write.RelativeElement(null, dx, 1, 2, {"type": "bar", "pitch2":10, linewidth:0.6});
908
- abselem.addRight(anchor);
909
- }
910
-
911
- if (elem.type==="bar_invisible") {
912
- anchor = new ABCJS.write.RelativeElement(null, dx, 1, 2, {"type": "none", "pitch2":10, linewidth:0.6});
913
- abselem.addRight(anchor);
914
- }
915
-
916
- if (elem.decoration) {
917
- this.printDecoration(elem.decoration, 12, (thick)?3:1, abselem, 0, "down", 2);
918
- }
919
-
920
- if (thick) {
921
- dx+=4; //3 hardcoded;
922
- anchor = new ABCJS.write.RelativeElement(null, dx, 4, 2, {"type": "bar", "pitch2":10, linewidth:4});
923
- abselem.addRight(anchor);
924
- dx+=5;
925
- }
926
-
927
- // if (this.partstartelem && (thick || (firstthin && secondthin))) { // means end of nth part
928
- // this.partstartelem.anchor2=anchor;
929
- // this.partstartelem = null;
930
- // }
931
-
932
- if (this.partstartelem && elem.endEnding) {
933
- this.partstartelem.anchor2=anchor;
934
- this.partstartelem = null;
935
- }
936
-
937
- if (secondthin) {
938
- dx+=3; //3 hardcoded;
939
- anchor = new ABCJS.write.RelativeElement(null, dx, 1, 2, {"type": "bar", "pitch2":10, linewidth:0.6});
940
- abselem.addRight(anchor); // 3 is hardcoded
941
- }
942
-
943
- if (seconddots) {
944
- dx+=3; //3 hardcoded;
945
- abselem.addRight(new ABCJS.write.RelativeElement("dots.dot", dx, 1, 7));
946
- abselem.addRight(new ABCJS.write.RelativeElement("dots.dot", dx, 1, 5));
947
- } // 2 is hardcoded
948
-
949
- if (elem.startEnding) {
950
- this.partstartelem = new ABCJS.write.EndingElem(elem.startEnding, anchor, null);
951
- this.voice.addOther(this.partstartelem);
952
- }
953
-
954
- return abselem;
955
-
956
- };
957
-
958
- ABCJS.write.Layout.prototype.printClef = function(elem) {
959
- var clef = "clefs.G";
960
- var octave = 0;
961
- var abselem = new ABCJS.write.AbsoluteElement(elem,0,10, 'staff-extra');
962
- switch (elem.type) {
963
- case "treble": break;
964
- case "tenor": clef="clefs.C"; break;
965
- case "alto": clef="clefs.C"; break;
966
- case "bass": clef="clefs.F"; break;
967
- case 'treble+8': octave = 1; break;
968
- case 'tenor+8':clef="clefs.C"; octave = 1; break;
969
- case 'bass+8': clef="clefs.F"; octave = 1; break;
970
- case 'alto+8': clef="clefs.C"; octave = 1; break;
971
- case 'treble-8': octave = -1; break;
972
- case 'tenor-8':clef="clefs.C"; octave = -1; break;
973
- case 'bass-8': clef="clefs.F"; octave = -1; break;
974
- case 'alto-8': clef="clefs.C"; octave = -1; break;
975
- case 'none': clef=""; break;
976
- case 'perc': clef="clefs.perc"; break;
977
- default: abselem.addChild(new ABCJS.write.RelativeElement("clef="+elem.type, 0, 0, 0, {type:"debug"}));
978
- }
979
- // if (elem.verticalPos) {
980
- // pitch = elem.verticalPos;
981
- // }
982
- var dx =10;
983
- if (clef!=="") {
984
- abselem.addRight(new ABCJS.write.RelativeElement(clef, dx, this.glyphs.getSymbolWidth(clef), elem.clefPos));
985
- }
986
- if (octave!==0) {
987
- var scale= 2/3;
988
- var adjustspacing = (this.glyphs.getSymbolWidth(clef)-this.glyphs.getSymbolWidth("8")*scale)/2;
989
- abselem.addRight(new ABCJS.write.RelativeElement("8", dx+adjustspacing, this.glyphs.getSymbolWidth("8")*scale, (octave>0)?16:-2, {scalex:scale, scaley:scale}));
990
- }
991
-
992
- if (elem.stafflines===0) {
993
- this.stafflines = 0;
994
- } else {
995
- this.stafflines =elem.stafflines;
996
- }
997
-
998
- return abselem;
999
- };
1000
-
1001
-
1002
- ABCJS.write.Layout.prototype.printKeySignature = function(elem) {
1003
- var abselem = new ABCJS.write.AbsoluteElement(elem,0,10, 'staff-extra');
1004
- var dx = 0;
1005
- if (elem.accidentals) {
1006
- window.ABCJS.parse.each(elem.accidentals, function(acc) {
1007
- var symbol = (acc.acc === "sharp") ? "accidentals.sharp" : (acc.acc === "natural") ? "accidentals.nat" : "accidentals.flat";
1008
- //var notes = { 'A': 5, 'B': 6, 'C': 0, 'D': 1, 'E': 2, 'F': 3, 'G':4, 'a': 12, 'b': 13, 'c': 7, 'd': 8, 'e': 9, 'f': 10, 'g':11 };
1009
- abselem.addRight(new ABCJS.write.RelativeElement(symbol, dx, this.glyphs.getSymbolWidth(symbol), acc.verticalPos));
1010
- dx += this.glyphs.getSymbolWidth(symbol)+2;
1011
- }, this);
1012
- }
1013
- this.startlimitelem = abselem; // limit ties here
1014
- return abselem;
1015
- };
1016
-
1017
- ABCJS.write.Layout.prototype.printTimeSignature= function(elem) {
1018
-
1019
- var abselem = new ABCJS.write.AbsoluteElement(elem,0,20, 'staff-extra');
1020
- if (elem.type === "specified") {
1021
- //TODO make the alignment for time signatures centered
1022
- for (var i = 0; i < elem.value.length; i++) {
1023
- if (i !== 0)
1024
- abselem.addRight(new ABCJS.write.RelativeElement('+', i*20-9, this.glyphs.getSymbolWidth("+"), 7));
1025
- if (elem.value[i].den) {
1026
- abselem.addRight(new ABCJS.write.RelativeElement(elem.value[i].num, i*20, this.glyphs.getSymbolWidth(elem.value[i].num.charAt(0))*elem.value[i].num.length, 9));
1027
- abselem.addRight(new ABCJS.write.RelativeElement(elem.value[i].den, i*20, this.glyphs.getSymbolWidth(elem.value[i].den.charAt(0))*elem.value[i].den.length, 5));
1028
- } else {
1029
- abselem.addRight(new ABCJS.write.RelativeElement(elem.value[i].num, i*20, this.glyphs.getSymbolWidth(elem.value[i].num.charAt(0))*elem.value[i].num.length, 7));
1030
- }
1031
- }
1032
- } else if (elem.type === "common_time") {
1033
- abselem.addRight(new ABCJS.write.RelativeElement("timesig.common", 0, this.glyphs.getSymbolWidth("timesig.common"), 7));
1034
-
1035
- } else if (elem.type === "cut_time") {
1036
- abselem.addRight(new ABCJS.write.RelativeElement("timesig.cut", 0, this.glyphs.getSymbolWidth("timesig.cut"), 7));
1037
- }
1038
- this.startlimitelem = abselem; // limit ties here
1039
- return abselem;
1040
- };