abcjs-rails 2.0 → 2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/abcjs/api/abc_animation.js +0 -19
- data/app/assets/javascripts/abcjs/api/abc_tunebook.js +0 -5
- data/app/assets/javascripts/abcjs/edit/abc_editor.js +0 -10
- data/app/assets/javascripts/abcjs/parse/abc_parse.js +11 -35
- data/app/assets/javascripts/abcjs/parse/abc_parse_directive.js +2 -18
- data/app/assets/javascripts/abcjs/write/abc_absolute_element.js +9 -25
- data/app/assets/javascripts/abcjs/write/abc_abstract_engraver.js +21 -16
- data/app/assets/javascripts/abcjs/write/abc_beam_element.js +5 -94
- data/app/assets/javascripts/abcjs/write/abc_crescendo_element.js +0 -16
- data/app/assets/javascripts/abcjs/write/abc_decoration.js +3 -2
- data/app/assets/javascripts/abcjs/write/abc_dynamic_decoration.js +0 -8
- data/app/assets/javascripts/abcjs/write/abc_ending_element.js +0 -20
- data/app/assets/javascripts/abcjs/write/abc_engraver_controller.js +1 -1
- data/app/assets/javascripts/abcjs/write/abc_relative_element.js +9 -46
- data/app/assets/javascripts/abcjs/write/abc_renderer.js +1 -1
- data/app/assets/javascripts/abcjs/write/abc_staff_group_element.js +4 -67
- data/app/assets/javascripts/abcjs/write/abc_tie_element.js +0 -24
- data/app/assets/javascripts/abcjs/write/abc_triplet_element.js +0 -54
- data/app/assets/javascripts/abcjs/write/abc_voice_element.js +24 -13
- data/lib/abcjs-rails/version.rb +1 -1
- metadata +2 -4
- data/app/assets/javascripts/abcjs/write/abc_layout.js +0 -1040
- 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}
|
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
|
-
|
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]);
|
data/lib/abcjs-rails/version.rb
CHANGED
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.
|
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-
|
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
|
-
};
|