abcjs-rails 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/app/assets/javascripts/abcjs-rails.js +1 -0
- data/app/assets/javascripts/abcjs/api/abc_tunebook.js +158 -0
- data/app/assets/javascripts/abcjs/data/abc_tune.js +686 -0
- data/app/assets/javascripts/abcjs/edit/abc_editor.js +414 -0
- data/app/assets/javascripts/abcjs/midi/abc_midiwriter.js +698 -0
- data/app/assets/javascripts/abcjs/parse/abc_common.js +76 -0
- data/app/assets/javascripts/abcjs/parse/abc_parse.js +1385 -0
- data/app/assets/javascripts/abcjs/parse/abc_parse_directive.js +546 -0
- data/app/assets/javascripts/abcjs/parse/abc_parse_header.js +521 -0
- data/app/assets/javascripts/abcjs/parse/abc_parse_key_voice.js +781 -0
- data/app/assets/javascripts/abcjs/parse/abc_tokenizer.js +751 -0
- data/app/assets/javascripts/abcjs/write/abc_glyphs.js +105 -0
- data/app/assets/javascripts/abcjs/write/abc_graphelements.js +781 -0
- data/app/assets/javascripts/abcjs/write/abc_layout.js +959 -0
- data/app/assets/javascripts/abcjs/write/abc_write.js +487 -0
- data/app/assets/javascripts/abcjs/write/raphael.js +3395 -0
- data/app/assets/javascripts/abcjs/write/sprintf.js +61 -0
- data/lib/abcjs-rails/version.rb +1 -1
- metadata +18 -1
@@ -0,0 +1,959 @@
|
|
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
|
+
return Math.floor(Math.log(duration)/Math.log(2));
|
35
|
+
};
|
36
|
+
|
37
|
+
ABCJS.write.Layout = function(glyphs, bagpipes) {
|
38
|
+
this.glyphs = glyphs;
|
39
|
+
this.isBagpipes = bagpipes;
|
40
|
+
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"},
|
41
|
+
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"},
|
42
|
+
uflags:{3:"flags.u8th", 4:"flags.u16th", 5:"flags.u32nd", 6:"flags.u64th"},
|
43
|
+
dflags:{3:"flags.d8th", 4:"flags.d16th", 5:"flags.d32nd", 6:"flags.d64th"}};
|
44
|
+
this.slurs = {};
|
45
|
+
this.ties = [];
|
46
|
+
this.slursbyvoice = {};
|
47
|
+
this.tiesbyvoice = {};
|
48
|
+
this.endingsbyvoice = {};
|
49
|
+
this.s = 0; // current staff number
|
50
|
+
this.v = 0; // current voice number on current staff
|
51
|
+
this.stafflines = 5;
|
52
|
+
};
|
53
|
+
|
54
|
+
ABCJS.write.Layout.prototype.getCurrentVoiceId = function() {
|
55
|
+
return "s"+this.s+"v"+this.v;
|
56
|
+
};
|
57
|
+
|
58
|
+
ABCJS.write.Layout.prototype.pushCrossLineElems = function() {
|
59
|
+
this.slursbyvoice[this.getCurrentVoiceId()] = this.slurs;
|
60
|
+
this.tiesbyvoice[this.getCurrentVoiceId()] = this.ties;
|
61
|
+
this.endingsbyvoice[this.getCurrentVoiceId()] = this.partstartelem;
|
62
|
+
};
|
63
|
+
|
64
|
+
ABCJS.write.Layout.prototype.popCrossLineElems = function() {
|
65
|
+
this.slurs = this.slursbyvoice[this.getCurrentVoiceId()] || {};
|
66
|
+
this.ties = this.tiesbyvoice[this.getCurrentVoiceId()] || [];
|
67
|
+
this.partstartelem = this.endingsbyvoice[this.getCurrentVoiceId()];
|
68
|
+
};
|
69
|
+
|
70
|
+
ABCJS.write.Layout.prototype.getElem = function() {
|
71
|
+
if (this.abcline.length <= this.pos)
|
72
|
+
return null;
|
73
|
+
return this.abcline[this.pos];
|
74
|
+
};
|
75
|
+
|
76
|
+
ABCJS.write.Layout.prototype.getNextElem = function() {
|
77
|
+
if (this.abcline.length <= this.pos+1)
|
78
|
+
return null;
|
79
|
+
return this.abcline[this.pos+1];
|
80
|
+
};
|
81
|
+
|
82
|
+
ABCJS.write.Layout.prototype.printABCLine = function(staffs) {
|
83
|
+
this.minY = 2; // PER: This is the lowest that any note reaches. It will be used to set the dynamics row.
|
84
|
+
this.staffgroup = new ABCJS.write.StaffGroupElement();
|
85
|
+
for (this.s = 0; this.s < staffs.length; this.s++) {
|
86
|
+
this.printABCStaff(staffs[this.s]);
|
87
|
+
}
|
88
|
+
return this.staffgroup;
|
89
|
+
};
|
90
|
+
|
91
|
+
ABCJS.write.Layout.prototype.printABCStaff = function(abcstaff) {
|
92
|
+
|
93
|
+
var header = "";
|
94
|
+
if (abcstaff.bracket) header += "bracket "+abcstaff.bracket+" ";
|
95
|
+
if (abcstaff.brace) header += "brace "+abcstaff.brace+" ";
|
96
|
+
|
97
|
+
|
98
|
+
for (this.v = 0; this.v < abcstaff.voices.length; this.v++) {
|
99
|
+
this.voice = new ABCJS.write.VoiceElement(this.v,abcstaff.voices.length);
|
100
|
+
if (this.v===0) {
|
101
|
+
this.voice.barfrom = (abcstaff.connectBarLines==="start" || abcstaff.connectBarLines==="continue");
|
102
|
+
this.voice.barto = (abcstaff.connectBarLines==="continue" || abcstaff.connectBarLines==="end");
|
103
|
+
} else {
|
104
|
+
this.voice.duplicate = true; // barlines and other duplicate info need not be printed
|
105
|
+
}
|
106
|
+
if (abcstaff.title && abcstaff.title[this.v]) this.voice.header=abcstaff.title[this.v];
|
107
|
+
// TODO make invisible if voice is duplicate
|
108
|
+
this.voice.addChild(this.printClef(abcstaff.clef));
|
109
|
+
this.voice.addChild(this.printKeySignature(abcstaff.key));
|
110
|
+
if (abcstaff.meter) this.voice.addChild(this.printTimeSignature(abcstaff.meter));
|
111
|
+
this.printABCVoice(abcstaff.voices[this.v]);
|
112
|
+
this.staffgroup.addVoice(this.voice,this.s,this.stafflines);
|
113
|
+
}
|
114
|
+
|
115
|
+
};
|
116
|
+
|
117
|
+
ABCJS.write.Layout.prototype.printABCVoice = function(abcline) {
|
118
|
+
this.popCrossLineElems();
|
119
|
+
this.stemdir = (this.isBagpipes)?"down":null;
|
120
|
+
this.abcline = abcline;
|
121
|
+
if (this.partstartelem) {
|
122
|
+
this.partstartelem = new ABCJS.write.EndingElem("", null, null);
|
123
|
+
this.voice.addOther(this.partstartelem);
|
124
|
+
}
|
125
|
+
for (var slur in this.slurs) {
|
126
|
+
if (this.slurs.hasOwnProperty(slur)) {
|
127
|
+
this.slurs[slur]= new ABCJS.write.TieElem(null, null, this.slurs[slur].above, this.slurs[slur].force);
|
128
|
+
this.voice.addOther(this.slurs[slur]);
|
129
|
+
}
|
130
|
+
}
|
131
|
+
for (var i=0; i<this.ties.length; i++) {
|
132
|
+
this.ties[i]=new ABCJS.write.TieElem(null, null, this.ties[i].above, this.ties[i].force);
|
133
|
+
this.voice.addOther(this.ties[i]);
|
134
|
+
}
|
135
|
+
|
136
|
+
for (this.pos=0; this.pos<this.abcline.length; this.pos++) {
|
137
|
+
var abselems = this.printABCElement();
|
138
|
+
for (i=0; i<abselems.length; i++) {
|
139
|
+
this.voice.addChild(abselems[i]);
|
140
|
+
}
|
141
|
+
}
|
142
|
+
this.pushCrossLineElems();
|
143
|
+
};
|
144
|
+
|
145
|
+
|
146
|
+
// return an array of ABCJS.write.AbsoluteElement
|
147
|
+
ABCJS.write.Layout.prototype.printABCElement = function() {
|
148
|
+
var elemset = [];
|
149
|
+
var elem = this.getElem();
|
150
|
+
switch (elem.el_type) {
|
151
|
+
case "note":
|
152
|
+
elemset = this.printBeam();
|
153
|
+
break;
|
154
|
+
case "bar":
|
155
|
+
elemset[0] = this.printBarLine(elem);
|
156
|
+
if (this.voice.duplicate) elemset[0].invisible = true;
|
157
|
+
break;
|
158
|
+
case "meter":
|
159
|
+
elemset[0] = this.printTimeSignature(elem);
|
160
|
+
if (this.voice.duplicate) elemset[0].invisible = true;
|
161
|
+
break;
|
162
|
+
case "clef":
|
163
|
+
elemset[0] = this.printClef(elem);
|
164
|
+
if (this.voice.duplicate) elemset[0].invisible = true;
|
165
|
+
break;
|
166
|
+
case "key":
|
167
|
+
elemset[0] = this.printKeySignature(elem);
|
168
|
+
if (this.voice.duplicate) elemset[0].invisible = true;
|
169
|
+
break;
|
170
|
+
case "stem":
|
171
|
+
this.stemdir=elem.direction;
|
172
|
+
break;
|
173
|
+
case "part":
|
174
|
+
var abselem = new ABCJS.write.AbsoluteElement(elem,0,0);
|
175
|
+
abselem.addChild(new ABCJS.write.RelativeElement(elem.title, 0, 0, 18, {type:"text", attributes:{"font-weight":"bold", "font-size":"16px", "font-family":"serif"}}));
|
176
|
+
elemset[0] = abselem;
|
177
|
+
break;
|
178
|
+
// case "tempo":
|
179
|
+
// this.printer.y = this.printer.printTempo(elem, this.printer.paper, this.printer.layouter, this.printer.y, this.printer, this.printer.x);
|
180
|
+
// break;
|
181
|
+
default:
|
182
|
+
var abselem2 = new ABCJS.write.AbsoluteElement(elem,0,0);
|
183
|
+
abselem2.addChild(new ABCJS.write.RelativeElement("element type "+elem.el_type, 0, 0, 0, {type:"debug"}));
|
184
|
+
elemset[0] = abselem2;
|
185
|
+
}
|
186
|
+
|
187
|
+
return elemset;
|
188
|
+
};
|
189
|
+
|
190
|
+
ABCJS.write.Layout.prototype.printBeam = function() {
|
191
|
+
var abselemset = [];
|
192
|
+
|
193
|
+
if (this.getElem().startBeam && !this.getElem().endBeam) {
|
194
|
+
var beamelem = new ABCJS.write.BeamElem(this.stemdir);
|
195
|
+
// PER: need two passes: the first one decides if the stems are up or down.
|
196
|
+
// TODO-PER: This could be more efficient.
|
197
|
+
var oldPos = this.pos;
|
198
|
+
var abselem;
|
199
|
+
while (this.getElem()) {
|
200
|
+
abselem = this.printNote(this.getElem(),true,true);
|
201
|
+
beamelem.add(abselem);
|
202
|
+
if (this.getElem().endBeam)
|
203
|
+
break;
|
204
|
+
this.pos++;
|
205
|
+
}
|
206
|
+
var dir = beamelem.calcDir();
|
207
|
+
this.pos = oldPos;
|
208
|
+
|
209
|
+
beamelem = new ABCJS.write.BeamElem(dir ? "up" : "down");
|
210
|
+
var oldDir = this.stemdir;
|
211
|
+
this.stemdir = dir ? "up" : "down";
|
212
|
+
while (this.getElem()) {
|
213
|
+
abselem = this.printNote(this.getElem(),true);
|
214
|
+
abselemset.push(abselem);
|
215
|
+
beamelem.add(abselem);
|
216
|
+
if (this.getElem().endBeam) {
|
217
|
+
break;
|
218
|
+
}
|
219
|
+
this.pos++;
|
220
|
+
}
|
221
|
+
this.stemdir = oldDir;
|
222
|
+
this.voice.addOther(beamelem);
|
223
|
+
} else {
|
224
|
+
abselemset[0] = this.printNote(this.getElem());
|
225
|
+
}
|
226
|
+
return abselemset;
|
227
|
+
};
|
228
|
+
|
229
|
+
ABCJS.write.sortPitch = function(elem) {
|
230
|
+
var sorted;
|
231
|
+
do {
|
232
|
+
sorted = true;
|
233
|
+
for (var p = 0; p<elem.pitches.length-1; p++) {
|
234
|
+
if (elem.pitches[p].pitch>elem.pitches[p+1].pitch) {
|
235
|
+
sorted = false;
|
236
|
+
var tmp = elem.pitches[p];
|
237
|
+
elem.pitches[p] = elem.pitches[p+1];
|
238
|
+
elem.pitches[p+1] = tmp;
|
239
|
+
}
|
240
|
+
}
|
241
|
+
} while (!sorted);
|
242
|
+
};
|
243
|
+
|
244
|
+
ABCJS.write.Layout.prototype.printNote = function(elem, nostem, dontDraw) { //stem presence: true for drawing stemless notehead
|
245
|
+
var notehead = null;
|
246
|
+
var grace= null;
|
247
|
+
this.roomtaken = 0; // room needed to the left of the note
|
248
|
+
this.roomtakenright = 0; // room needed to the right of the note
|
249
|
+
var dotshiftx = 0; // room taken by chords with displaced noteheads which cause dots to shift
|
250
|
+
var c="";
|
251
|
+
var flag = null;
|
252
|
+
var additionalLedgers = []; // PER: handle the case of [bc'], where the b doesn't have a ledger line
|
253
|
+
|
254
|
+
var p, i, pp;
|
255
|
+
var width, p1, p2, dx;
|
256
|
+
|
257
|
+
var duration = ABCJS.write.getDuration(elem);
|
258
|
+
var durlog = Math.floor(Math.log(duration)/Math.log(2));
|
259
|
+
var dot=0;
|
260
|
+
|
261
|
+
for (var tot = Math.pow(2,durlog), inc=tot/2; tot<duration; dot++,tot+=inc,inc/=2);
|
262
|
+
|
263
|
+
var abselem = new ABCJS.write.AbsoluteElement(elem, duration, 1);
|
264
|
+
|
265
|
+
|
266
|
+
if (elem.rest) {
|
267
|
+
var restpitch = 7;
|
268
|
+
if (this.stemdir==="down") restpitch = 3;
|
269
|
+
if (this.stemdir==="up") restpitch = 11;
|
270
|
+
switch(elem.rest.type) {
|
271
|
+
case "rest":
|
272
|
+
c = this.chartable.rest[-durlog];
|
273
|
+
elem.averagepitch=restpitch;
|
274
|
+
elem.minpitch=restpitch;
|
275
|
+
elem.maxpitch=restpitch;
|
276
|
+
break;
|
277
|
+
case "invisible":
|
278
|
+
case "spacer":
|
279
|
+
c="";
|
280
|
+
}
|
281
|
+
if (!dontDraw)
|
282
|
+
notehead = this.printNoteHead(abselem, c, {verticalPos:restpitch}, null, 0, -this.roomtaken, null, dot, 0, 1);
|
283
|
+
if (notehead) abselem.addHead(notehead);
|
284
|
+
this.roomtaken+=this.accidentalshiftx;
|
285
|
+
this.roomtakenright = Math.max(this.roomtakenright,this.dotshiftx);
|
286
|
+
|
287
|
+
} else {
|
288
|
+
ABCJS.write.sortPitch(elem);
|
289
|
+
|
290
|
+
// determine averagepitch, minpitch, maxpitch and stem direction
|
291
|
+
var sum=0;
|
292
|
+
for (p=0, pp=elem.pitches.length; p<pp; p++) {
|
293
|
+
sum += elem.pitches[p].verticalPos;
|
294
|
+
}
|
295
|
+
elem.averagepitch = sum/elem.pitches.length;
|
296
|
+
elem.minpitch = elem.pitches[0].verticalPos;
|
297
|
+
this.minY = Math.min(elem.minpitch, this.minY);
|
298
|
+
elem.maxpitch = elem.pitches[elem.pitches.length-1].verticalPos;
|
299
|
+
var dir = (elem.averagepitch>=6) ? "down": "up";
|
300
|
+
if (this.stemdir) dir=this.stemdir;
|
301
|
+
|
302
|
+
// determine elements of chords which should be shifted
|
303
|
+
for (p=(dir==="down")?elem.pitches.length-2:1; (dir==="down")?p>=0:p<elem.pitches.length; p=(dir==="down")?p-1:p+1) {
|
304
|
+
var prev = elem.pitches[(dir==="down")?p+1:p-1];
|
305
|
+
var curr = elem.pitches[p];
|
306
|
+
var delta = (dir==="down")?prev.pitch-curr.pitch:curr.pitch-prev.pitch;
|
307
|
+
if (delta<=1 && !prev.printer_shift) {
|
308
|
+
curr.printer_shift=(delta)?"different":"same";
|
309
|
+
if (curr.pitch > 11 || curr.pitch < 1) { // PER: add extra ledger line
|
310
|
+
additionalLedgers.push(curr.pitch - (curr.pitch%2));
|
311
|
+
}
|
312
|
+
if (dir==="down") {
|
313
|
+
this.roomtaken = this.glyphs.getSymbolWidth(this.chartable.note[-durlog])+2;
|
314
|
+
} else {
|
315
|
+
dotshiftx = this.glyphs.getSymbolWidth(this.chartable.note[-durlog])+2;
|
316
|
+
}
|
317
|
+
}
|
318
|
+
}
|
319
|
+
|
320
|
+
// The accidentalSlot will hold a list of all the accidentals on this chord. Each element is a vertical place,
|
321
|
+
// and contains a pitch, which is the last pitch that contains an accidental in that slot. The slots are numbered
|
322
|
+
// from closest to the note to farther left. We only need to know the last accidental we placed because
|
323
|
+
// we know that the pitches are sorted by now.
|
324
|
+
this.accidentalSlot = [];
|
325
|
+
|
326
|
+
for (p=0; p<elem.pitches.length; p++) {
|
327
|
+
|
328
|
+
if (!nostem) {
|
329
|
+
if ((dir==="down" && p!==0) || (dir==="up" && p!==pp-1)) { // not the stemmed elem of the chord
|
330
|
+
flag = null;
|
331
|
+
} else {
|
332
|
+
flag = this.chartable[(dir==="down")?"dflags":"uflags"][-durlog];
|
333
|
+
}
|
334
|
+
c = this.chartable.note[-durlog];
|
335
|
+
} else {
|
336
|
+
c="noteheads.quarter";
|
337
|
+
}
|
338
|
+
|
339
|
+
// 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.
|
340
|
+
// If the note is the only one in the chord, then any slur it has counts as if it were on the whole chord.
|
341
|
+
elem.pitches[p].highestVert = elem.pitches[p].verticalPos;
|
342
|
+
var isTopWhenStemIsDown = (this.stemdir==="up" || dir==="up") && p===0;
|
343
|
+
var isBottomWhenStemIsUp = (this.stemdir==="down" || dir==="down") && p===pp-1;
|
344
|
+
if (!dontDraw && (isTopWhenStemIsDown || isBottomWhenStemIsUp)) { // place to put slurs if not already on pitches
|
345
|
+
|
346
|
+
if (elem.startSlur || pp === 1) {
|
347
|
+
elem.pitches[p].highestVert = elem.pitches[pp-1].verticalPos;
|
348
|
+
if (this.stemdir==="up" || dir==="up")
|
349
|
+
elem.pitches[p].highestVert += 6; // If the stem is up, then compensate for the length of the stem
|
350
|
+
}
|
351
|
+
if (elem.startSlur) {
|
352
|
+
if (!elem.pitches[p].startSlur) elem.pitches[p].startSlur = []; //TODO possibly redundant, provided array is not optional
|
353
|
+
for (i=0; i<elem.startSlur.length; i++) {
|
354
|
+
elem.pitches[p].startSlur.push(elem.startSlur[i]);
|
355
|
+
}
|
356
|
+
}
|
357
|
+
|
358
|
+
if (!dontDraw && elem.endSlur) {
|
359
|
+
elem.pitches[p].highestVert = elem.pitches[pp-1].verticalPos;
|
360
|
+
if (this.stemdir==="up" || dir==="up")
|
361
|
+
elem.pitches[p].highestVert += 6; // If the stem is up, then compensate for the length of the stem
|
362
|
+
if (!elem.pitches[p].endSlur) elem.pitches[p].endSlur = []; //TODO possibly redundant, provided array is not optional
|
363
|
+
for (i=0; i<elem.endSlur.length; i++) {
|
364
|
+
elem.pitches[p].endSlur.push(elem.endSlur[i]);
|
365
|
+
}
|
366
|
+
}
|
367
|
+
}
|
368
|
+
|
369
|
+
if (!dontDraw)
|
370
|
+
notehead = this.printNoteHead(abselem, c, elem.pitches[p], dir, 0, -this.roomtaken, flag, dot, dotshiftx, 1);
|
371
|
+
if (notehead) abselem.addHead(notehead);
|
372
|
+
this.roomtaken += this.accidentalshiftx;
|
373
|
+
this.roomtakenright = Math.max(this.roomtakenright,this.dotshiftx);
|
374
|
+
}
|
375
|
+
|
376
|
+
// draw stem from the furthest note to a pitch above/below the stemmed note
|
377
|
+
if (!nostem && durlog<=-1) {
|
378
|
+
p1 = (dir==="down") ? elem.minpitch-7 : elem.minpitch+1/3;
|
379
|
+
// PER added stemdir test to make the line meet the note.
|
380
|
+
if (p1>6 && !this.stemdir) p1=6;
|
381
|
+
p2 = (dir==="down") ? elem.maxpitch-1/3 : elem.maxpitch+7;
|
382
|
+
// PER added stemdir test to make the line meet the note.
|
383
|
+
if (p2<6 && !this.stemdir) p2=6;
|
384
|
+
dx = (dir==="down" || abselem.heads.length === 0)?0:abselem.heads[0].w;
|
385
|
+
width = (dir==="down")?1:-1;
|
386
|
+
abselem.addExtra(new ABCJS.write.RelativeElement(null, dx, 0, p1, {"type": "stem", "pitch2":p2, linewidth: width}));
|
387
|
+
this.minY = Math.min(p1, this.minY);
|
388
|
+
this.minY = Math.min(p2, this.minY);
|
389
|
+
}
|
390
|
+
|
391
|
+
}
|
392
|
+
|
393
|
+
if (elem.lyric !== undefined) {
|
394
|
+
var lyricStr = "";
|
395
|
+
window.ABCJS.parse.each(elem.lyric, function(ly) {
|
396
|
+
lyricStr += ly.syllable + ly.divider + "\n";
|
397
|
+
});
|
398
|
+
abselem.addRight(new ABCJS.write.RelativeElement(lyricStr, 0, lyricStr.length*5, 0, {type:"debugLow"}));
|
399
|
+
}
|
400
|
+
|
401
|
+
if (!dontDraw && elem.gracenotes !== undefined) {
|
402
|
+
var gracescale = 3/5;
|
403
|
+
var gracebeam = null;
|
404
|
+
if (elem.gracenotes.length>1) {
|
405
|
+
gracebeam = new ABCJS.write.BeamElem("grace",this.isBagpipes);
|
406
|
+
}
|
407
|
+
|
408
|
+
var graceoffsets = [];
|
409
|
+
for (i=elem.gracenotes.length-1; i>=0; i--) { // figure out where to place each gracenote
|
410
|
+
this.roomtaken+=10;
|
411
|
+
graceoffsets[i] = this.roomtaken;
|
412
|
+
if (elem.gracenotes[i].accidental) {
|
413
|
+
this.roomtaken+=7;
|
414
|
+
}
|
415
|
+
}
|
416
|
+
|
417
|
+
for (i=0; i<elem.gracenotes.length; i++) {
|
418
|
+
var gracepitch = elem.gracenotes[i].verticalPos;
|
419
|
+
|
420
|
+
flag = (gracebeam) ? null : this.chartable.uflags[(this.isBagpipes)?5:3];
|
421
|
+
grace = this.printNoteHead(abselem, "noteheads.quarter", elem.gracenotes[i], "up", -graceoffsets[i], -graceoffsets[i], flag, 0, 0, gracescale);
|
422
|
+
abselem.addExtra(grace);
|
423
|
+
// PER: added acciaccatura slash
|
424
|
+
if (elem.gracenotes[i].acciaccatura) {
|
425
|
+
var pos = elem.gracenotes[i].verticalPos+7*gracescale; // the same formula that determines the flag position.
|
426
|
+
var dAcciaccatura = gracebeam ? 5 : 6; // just an offset to make it line up correctly.
|
427
|
+
abselem.addRight(new ABCJS.write.RelativeElement("flags.ugrace", -graceoffsets[i]+dAcciaccatura, 0, pos, {scalex:gracescale, scaley: gracescale}));
|
428
|
+
}
|
429
|
+
if (gracebeam) { // give the beam the necessary info
|
430
|
+
var pseudoabselem = {heads:[grace],
|
431
|
+
abcelem:{averagepitch: gracepitch, minpitch: gracepitch, maxpitch: gracepitch},
|
432
|
+
duration:(this.isBagpipes)?1/32:1/16};
|
433
|
+
gracebeam.add(pseudoabselem);
|
434
|
+
} else { // draw the stem
|
435
|
+
p1 = gracepitch+1/3*gracescale;
|
436
|
+
p2 = gracepitch+7*gracescale;
|
437
|
+
dx = grace.dx + grace.w;
|
438
|
+
width = -0.6;
|
439
|
+
abselem.addExtra(new ABCJS.write.RelativeElement(null, dx, 0, p1, {"type": "stem", "pitch2":p2, linewidth: width}));
|
440
|
+
}
|
441
|
+
|
442
|
+
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));
|
443
|
+
}
|
444
|
+
|
445
|
+
if (gracebeam) {
|
446
|
+
this.voice.addOther(gracebeam);
|
447
|
+
}
|
448
|
+
}
|
449
|
+
|
450
|
+
if (!dontDraw && elem.decoration) {
|
451
|
+
var addMark = this.printDecoration(elem.decoration, elem.maxpitch, (notehead)?notehead.w:0, abselem, this.roomtaken, dir, elem.minpitch);
|
452
|
+
if (addMark) {
|
453
|
+
abselem.klass = "mark";
|
454
|
+
}
|
455
|
+
}
|
456
|
+
|
457
|
+
if (elem.barNumber) {
|
458
|
+
abselem.addChild(new ABCJS.write.RelativeElement(elem.barNumber, -10, 0, 0, {type:"debug"}));
|
459
|
+
}
|
460
|
+
|
461
|
+
// ledger lines
|
462
|
+
for (i=elem.maxpitch; i>11; i--) {
|
463
|
+
if (i%2===0 && !elem.rest) {
|
464
|
+
abselem.addChild(new ABCJS.write.RelativeElement(null, -2, this.glyphs.getSymbolWidth(c)+4, i, {type:"ledger"}));
|
465
|
+
}
|
466
|
+
}
|
467
|
+
|
468
|
+
for (i=elem.minpitch; i<1; i++) {
|
469
|
+
if (i%2===0 && !elem.rest) {
|
470
|
+
abselem.addChild(new ABCJS.write.RelativeElement(null, -2, this.glyphs.getSymbolWidth(c)+4, i, {type:"ledger"}));
|
471
|
+
}
|
472
|
+
}
|
473
|
+
|
474
|
+
for (i = 0; i < additionalLedgers.length; i++) { // PER: draw additional ledgers
|
475
|
+
var ofs = this.glyphs.getSymbolWidth(c);
|
476
|
+
if (dir === 'down') ofs = -ofs;
|
477
|
+
abselem.addChild(new ABCJS.write.RelativeElement(null, ofs-2, this.glyphs.getSymbolWidth(c)+4, additionalLedgers[i], {type:"ledger"}));
|
478
|
+
}
|
479
|
+
|
480
|
+
if (elem.chord !== undefined) { //16 -> high E.
|
481
|
+
for (i = 0; i < elem.chord.length; i++) {
|
482
|
+
var x = 0;
|
483
|
+
var y = 16;
|
484
|
+
switch (elem.chord[i].position) {
|
485
|
+
case "left":
|
486
|
+
this.roomtaken+=7;
|
487
|
+
x = -this.roomtaken; // TODO-PER: This is just a guess from trial and error
|
488
|
+
y = elem.averagepitch;
|
489
|
+
abselem.addExtra(new ABCJS.write.RelativeElement(elem.chord[i].name, x, this.glyphs.getSymbolWidth(elem.chord[i].name[0])+4, y, {type:"text"}));
|
490
|
+
break;
|
491
|
+
case "right":
|
492
|
+
this.roomtakenright+=4;
|
493
|
+
x = this.roomtakenright;// TODO-PER: This is just a guess from trial and error
|
494
|
+
y = elem.averagepitch;
|
495
|
+
abselem.addRight(new ABCJS.write.RelativeElement(elem.chord[i].name, x, this.glyphs.getSymbolWidth(elem.chord[i].name[0])+4, y, {type:"text"}));
|
496
|
+
break;
|
497
|
+
case "below":
|
498
|
+
y = -3;
|
499
|
+
abselem.addChild(new ABCJS.write.RelativeElement(elem.chord[i].name, x, 0, y, {type:"text"}));
|
500
|
+
break;
|
501
|
+
default:
|
502
|
+
abselem.addChild(new ABCJS.write.RelativeElement(elem.chord[i].name, x, 0, y, {type:"text"}));
|
503
|
+
}
|
504
|
+
}
|
505
|
+
}
|
506
|
+
|
507
|
+
|
508
|
+
if (elem.startTriplet) {
|
509
|
+
this.triplet = new ABCJS.write.TripletElem(elem.startTriplet, notehead, null, true); // above is opposite from case of slurs
|
510
|
+
if (!dontDraw)
|
511
|
+
this.voice.addOther(this.triplet);
|
512
|
+
}
|
513
|
+
|
514
|
+
if (elem.endTriplet && this.triplet) {
|
515
|
+
this.triplet.anchor2 = notehead;
|
516
|
+
this.triplet = null;
|
517
|
+
}
|
518
|
+
|
519
|
+
return abselem;
|
520
|
+
};
|
521
|
+
|
522
|
+
|
523
|
+
|
524
|
+
|
525
|
+
ABCJS.write.Layout.prototype.printNoteHead = function(abselem, c, pitchelem, dir, headx, extrax, flag, dot, dotshiftx, scale) {
|
526
|
+
|
527
|
+
// TODO scale the dot as well
|
528
|
+
var pitch = pitchelem.verticalPos;
|
529
|
+
var notehead;
|
530
|
+
var i;
|
531
|
+
this.accidentalshiftx = 0;
|
532
|
+
this.dotshiftx = 0;
|
533
|
+
if (c === undefined)
|
534
|
+
abselem.addChild(new ABCJS.write.RelativeElement("pitch is undefined", 0, 0, 0, {type:"debug"}));
|
535
|
+
else if (c==="") {
|
536
|
+
notehead = new ABCJS.write.RelativeElement(null, 0, 0, pitch);
|
537
|
+
} else {
|
538
|
+
var shiftheadx = headx;
|
539
|
+
if (pitchelem.printer_shift) {
|
540
|
+
var adjust = (pitchelem.printer_shift==="same")?1:0;
|
541
|
+
shiftheadx = (dir==="down")?-this.glyphs.getSymbolWidth(c)*scale+adjust:this.glyphs.getSymbolWidth(c)*scale-adjust;
|
542
|
+
}
|
543
|
+
notehead = new ABCJS.write.RelativeElement(c, shiftheadx, this.glyphs.getSymbolWidth(c)*scale, pitch, {scalex:scale, scaley: scale, extreme: ((dir==="down")?"below":"above")});
|
544
|
+
if (flag) {
|
545
|
+
var pos = pitch+((dir==="down")?-7:7)*scale;
|
546
|
+
if (scale===1 && (dir==="down")?(pos>6):(pos<6)) pos=6;
|
547
|
+
var xdelta = (dir==="down")?headx:headx+notehead.w-0.6;
|
548
|
+
abselem.addRight(new ABCJS.write.RelativeElement(flag, xdelta, this.glyphs.getSymbolWidth(flag)*scale, pos, {scalex:scale, scaley: scale}));
|
549
|
+
}
|
550
|
+
this.dotshiftx = notehead.w+dotshiftx-2+5*dot;
|
551
|
+
for (;dot>0;dot--) {
|
552
|
+
var dotadjusty = (1-Math.abs(pitch)%2); //PER: take abs value of the pitch. And the shift still happens on ledger lines.
|
553
|
+
abselem.addRight(new ABCJS.write.RelativeElement("dots.dot", notehead.w+dotshiftx-2+5*dot, this.glyphs.getSymbolWidth("dots.dot"), pitch+dotadjusty));
|
554
|
+
}
|
555
|
+
}
|
556
|
+
if (notehead)
|
557
|
+
notehead.highestVert = pitchelem.highestVert;
|
558
|
+
|
559
|
+
if (pitchelem.accidental) {
|
560
|
+
var symb;
|
561
|
+
switch (pitchelem.accidental) {
|
562
|
+
case "quartersharp":
|
563
|
+
symb = "accidentals.halfsharp";
|
564
|
+
break;
|
565
|
+
case "dblsharp":
|
566
|
+
symb = "accidentals.dblsharp";
|
567
|
+
break;
|
568
|
+
case "sharp":
|
569
|
+
symb = "accidentals.sharp";
|
570
|
+
break;
|
571
|
+
case "quarterflat":
|
572
|
+
symb = "accidentals.halfflat";
|
573
|
+
break;
|
574
|
+
case "flat":
|
575
|
+
symb = "accidentals.flat";
|
576
|
+
break;
|
577
|
+
case "dblflat":
|
578
|
+
symb = "accidentals.dblflat";
|
579
|
+
break;
|
580
|
+
case "natural":
|
581
|
+
symb = "accidentals.nat";
|
582
|
+
}
|
583
|
+
// if a note is at least a sixth away, it can share a slot with another accidental
|
584
|
+
var accSlotFound = false;
|
585
|
+
var accPlace = extrax;
|
586
|
+
for (var j = 0; j < this.accidentalSlot.length; j++) {
|
587
|
+
if (pitch - this.accidentalSlot[j][0] >= 6) {
|
588
|
+
this.accidentalSlot[j][0] = pitch;
|
589
|
+
accPlace = this.accidentalSlot[j][1];
|
590
|
+
accSlotFound = true;
|
591
|
+
break;
|
592
|
+
}
|
593
|
+
}
|
594
|
+
if (accSlotFound === false) {
|
595
|
+
accPlace -= (this.glyphs.getSymbolWidth(symb)*scale+2);
|
596
|
+
this.accidentalSlot.push([pitch,accPlace]);
|
597
|
+
this.accidentalshiftx = (this.glyphs.getSymbolWidth(symb)*scale+2);
|
598
|
+
}
|
599
|
+
abselem.addExtra(new ABCJS.write.RelativeElement(symb, accPlace, this.glyphs.getSymbolWidth(symb), pitch, {scalex:scale, scaley: scale}));
|
600
|
+
}
|
601
|
+
|
602
|
+
if (pitchelem.endTie) {
|
603
|
+
if (this.ties[0]) {
|
604
|
+
this.ties[0].anchor2=notehead;
|
605
|
+
this.ties = this.ties.slice(1,this.ties.length);
|
606
|
+
}
|
607
|
+
}
|
608
|
+
|
609
|
+
if (pitchelem.startTie) {
|
610
|
+
//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"));
|
611
|
+
var tie = new ABCJS.write.TieElem(notehead, null, (this.stemdir==="down" || dir==="down") && this.stemdir!=="up",(this.stemdir==="down" || this.stemdir==="up"));
|
612
|
+
this.ties[this.ties.length]=tie;
|
613
|
+
this.voice.addOther(tie);
|
614
|
+
}
|
615
|
+
|
616
|
+
if (pitchelem.endSlur) {
|
617
|
+
for (i=0; i<pitchelem.endSlur.length; i++) {
|
618
|
+
var slurid = pitchelem.endSlur[i];
|
619
|
+
var slur;
|
620
|
+
if (this.slurs[slurid]) {
|
621
|
+
slur = this.slurs[slurid].anchor2=notehead;
|
622
|
+
delete this.slurs[slurid];
|
623
|
+
} else {
|
624
|
+
slur = new ABCJS.write.TieElem(null, notehead, dir==="down",(this.stemdir==="up" || dir==="down") && this.stemdir!=="down", this.stemdir);
|
625
|
+
this.voice.addOther(slur);
|
626
|
+
}
|
627
|
+
if (this.startlimitelem) {
|
628
|
+
slur.startlimitelem = this.startlimitelem;
|
629
|
+
}
|
630
|
+
}
|
631
|
+
}
|
632
|
+
|
633
|
+
if (pitchelem.startSlur) {
|
634
|
+
for (i=0; i<pitchelem.startSlur.length; i++) {
|
635
|
+
var slurid = pitchelem.startSlur[i].label;
|
636
|
+
//PER: bug fix: var slur = new ABCJS.write.TieElem(notehead, null, (this.stemdir=="up" || dir=="down") && this.stemdir!="down", this.stemdir);
|
637
|
+
var slur = new ABCJS.write.TieElem(notehead, null, (this.stemdir==="down" || dir==="down") && this.stemdir!=="up", false);
|
638
|
+
this.slurs[slurid]=slur;
|
639
|
+
this.voice.addOther(slur);
|
640
|
+
}
|
641
|
+
}
|
642
|
+
|
643
|
+
return notehead;
|
644
|
+
|
645
|
+
};
|
646
|
+
|
647
|
+
ABCJS.write.Layout.prototype.printDecoration = function(decoration, pitch, width, abselem, roomtaken, dir, minPitch) {
|
648
|
+
var dec;
|
649
|
+
var compoundDec; // PER: for decorations with two symbols
|
650
|
+
var diminuendo;
|
651
|
+
var crescendo;
|
652
|
+
var unknowndecs = [];
|
653
|
+
var yslot = (pitch>9) ? pitch+3 : 12;
|
654
|
+
var ypos;
|
655
|
+
//var dir = (this.stemdir==="down" || pitch>=6) && this.stemdir!=="up";
|
656
|
+
var below = false; // PER: whether decoration goes above or below.
|
657
|
+
var yslotB = this.minY - 4; // (pitch<1) ? pitch-9 : -6;
|
658
|
+
var i;
|
659
|
+
roomtaken = roomtaken || 0;
|
660
|
+
if (pitch===5) yslot=14; // avoid upstem of the A
|
661
|
+
var addMark = false; // PER: to allow the user to add a class whereever
|
662
|
+
|
663
|
+
for (i=0;i<decoration.length; i++) { // treat staccato first (may need to shift other markers) //TODO, same with tenuto?
|
664
|
+
if (decoration[i]==="staccato") {
|
665
|
+
ypos = (dir==="down") ? pitch+2:minPitch-2;
|
666
|
+
// don't place on a stave line. The stave lines are 2,4,6,8,10
|
667
|
+
switch (ypos) {
|
668
|
+
case 2:
|
669
|
+
case 4:
|
670
|
+
case 6:
|
671
|
+
case 8:
|
672
|
+
case 10:
|
673
|
+
if (dir === "up") ypos--;
|
674
|
+
else ypos++;
|
675
|
+
break;
|
676
|
+
}
|
677
|
+
if (pitch>9) yslot++; // take up some room of those that are above
|
678
|
+
var deltax = width/2;
|
679
|
+
if (this.glyphs.getSymbolAlign("scripts.staccato")!=="center") {
|
680
|
+
deltax -= (this.glyphs.getSymbolWidth(dec)/2);
|
681
|
+
}
|
682
|
+
abselem.addChild(new ABCJS.write.RelativeElement("scripts.staccato", deltax, this.glyphs.getSymbolWidth("scripts.staccato"), ypos));
|
683
|
+
}
|
684
|
+
if (decoration[i]==="slide" && abselem.heads[0]) {
|
685
|
+
ypos = abselem.heads[0].pitch;
|
686
|
+
var blank1 = new ABCJS.write.RelativeElement("", -roomtaken-15, 0, ypos-1);
|
687
|
+
var blank2 = new ABCJS.write.RelativeElement("", -roomtaken-5, 0, ypos+1);
|
688
|
+
abselem.addChild(blank1);
|
689
|
+
abselem.addChild(blank2);
|
690
|
+
this.voice.addOther(new ABCJS.write.TieElem(blank1, blank2, false));
|
691
|
+
}
|
692
|
+
}
|
693
|
+
|
694
|
+
for (i=0;i<decoration.length; i++) {
|
695
|
+
below = false;
|
696
|
+
switch(decoration[i]) {
|
697
|
+
case "trill":dec="scripts.trill";break;
|
698
|
+
case "roll": dec="scripts.roll"; break; //TODO put abc2ps roll in here
|
699
|
+
case "irishroll": dec="scripts.roll"; break;
|
700
|
+
case "marcato": dec="scripts.umarcato"; break;
|
701
|
+
case "marcato2": dec="scriopts.dmarcato"; break;//other marcato
|
702
|
+
case "turn": dec="scripts.turn"; break;
|
703
|
+
case "uppermordent": dec="scripts.prall"; break;
|
704
|
+
case "mordent":
|
705
|
+
case "lowermordent": dec="scripts.mordent"; break;
|
706
|
+
case "staccato":
|
707
|
+
case "slide": continue;
|
708
|
+
case "downbow": dec="scripts.downbow";break;
|
709
|
+
case "upbow": dec="scripts.upbow";break;
|
710
|
+
case "fermata": dec="scripts.ufermata"; break;
|
711
|
+
case "invertedfermata": below = true; dec="scripts.dfermata"; break;
|
712
|
+
case "breath": dec=","; break;
|
713
|
+
case "accent": dec="scripts.sforzato"; break;
|
714
|
+
case "tenuto": dec="scripts.tenuto"; break;
|
715
|
+
case "coda": dec="scripts.coda"; break;
|
716
|
+
case "segno": dec="scripts.segno"; break;
|
717
|
+
case "/": compoundDec=["flags.ugrace", 1]; continue; // PER: added new decorations
|
718
|
+
case "//": compoundDec=["flags.ugrace", 2]; continue;
|
719
|
+
case "///": compoundDec=["flags.ugrace", 3]; continue;
|
720
|
+
case "////": compoundDec=["flags.ugrace", 4]; continue;
|
721
|
+
case "p":
|
722
|
+
case "mp":
|
723
|
+
case "pp":
|
724
|
+
case "ppp":
|
725
|
+
case "pppp":
|
726
|
+
case "f":
|
727
|
+
case "ff":
|
728
|
+
case "fff":
|
729
|
+
case "ffff":
|
730
|
+
case "sfz":
|
731
|
+
case "mf":
|
732
|
+
var ddelem = new ABCJS.write.DynamicDecoration(abselem, decoration[i]);
|
733
|
+
this.voice.addOther(ddelem);
|
734
|
+
continue;
|
735
|
+
case "mark": addMark = true; continue;
|
736
|
+
case "diminuendo(":
|
737
|
+
ABCJS.write.Layout.prototype.startDiminuendoX = abselem;
|
738
|
+
diminuendo = undefined;
|
739
|
+
continue;
|
740
|
+
case "diminuendo)":
|
741
|
+
diminuendo = { start: ABCJS.write.Layout.prototype.startDiminuendoX, stop: abselem};
|
742
|
+
ABCJS.write.Layout.prototype.startDiminuendoX = undefined;
|
743
|
+
continue;
|
744
|
+
case "crescendo(":
|
745
|
+
ABCJS.write.Layout.prototype.startCrescendoX = abselem;
|
746
|
+
crescendo = undefined;
|
747
|
+
continue;
|
748
|
+
case "crescendo)":
|
749
|
+
crescendo = { start: ABCJS.write.Layout.prototype.startCrescendoX, stop: abselem};
|
750
|
+
ABCJS.write.Layout.prototype.startCrescendoX = undefined;
|
751
|
+
continue;
|
752
|
+
default:
|
753
|
+
unknowndecs[unknowndecs.length]=decoration[i];
|
754
|
+
continue;
|
755
|
+
}
|
756
|
+
if (below) {
|
757
|
+
ypos = yslotB;
|
758
|
+
yslotB -= 4;
|
759
|
+
} else {
|
760
|
+
ypos=yslot;
|
761
|
+
yslot+=3;
|
762
|
+
}
|
763
|
+
var deltax = width/2;
|
764
|
+
if (this.glyphs.getSymbolAlign(dec)!=="center") {
|
765
|
+
deltax -= (this.glyphs.getSymbolWidth(dec)/2);
|
766
|
+
}
|
767
|
+
abselem.addChild(new ABCJS.write.RelativeElement(dec, deltax, this.glyphs.getSymbolWidth(dec), ypos));
|
768
|
+
}
|
769
|
+
if (compoundDec) { // PER: added new decorations
|
770
|
+
ypos = (dir) ? pitch+1:pitch+9;
|
771
|
+
deltax = width/2;
|
772
|
+
deltax += (dir) ? -5 : 3;
|
773
|
+
for (var xx = 0; xx < compoundDec[1]; xx++) {
|
774
|
+
ypos -= 1;
|
775
|
+
abselem.addChild(new ABCJS.write.RelativeElement(compoundDec[0], deltax, this.glyphs.getSymbolWidth(compoundDec[0]), ypos));
|
776
|
+
}
|
777
|
+
}
|
778
|
+
if (diminuendo) {
|
779
|
+
var delem = new ABCJS.write.CrescendoElem(diminuendo.start, diminuendo.stop, ">");
|
780
|
+
this.voice.addOther(delem);
|
781
|
+
}
|
782
|
+
if (crescendo) {
|
783
|
+
var celem = new ABCJS.write.CrescendoElem(crescendo.start, crescendo.stop, "<");
|
784
|
+
this.voice.addOther(celem);
|
785
|
+
}
|
786
|
+
if (unknowndecs.length>0)
|
787
|
+
abselem.addChild(new ABCJS.write.RelativeElement(unknowndecs.join(','), 0, 0, 0, {type:"debug"}));
|
788
|
+
return addMark;
|
789
|
+
};
|
790
|
+
|
791
|
+
ABCJS.write.Layout.prototype.printBarLine = function (elem) {
|
792
|
+
// bar_thin, bar_thin_thick, bar_thin_thin, bar_thick_thin, bar_right_repeat, bar_left_repeat, bar_double_repeat
|
793
|
+
|
794
|
+
var abselem = new ABCJS.write.AbsoluteElement(elem, 0, 10);
|
795
|
+
var anchor = null; // place to attach part lines
|
796
|
+
var dx = 0;
|
797
|
+
|
798
|
+
|
799
|
+
|
800
|
+
var firstdots = (elem.type==="bar_right_repeat" || elem.type==="bar_dbl_repeat");
|
801
|
+
var firstthin = (elem.type!=="bar_left_repeat" && elem.type!=="bar_thick_thin" && elem.type!=="bar_invisible");
|
802
|
+
var thick = (elem.type==="bar_right_repeat" || elem.type==="bar_dbl_repeat" || elem.type==="bar_left_repeat" ||
|
803
|
+
elem.type==="bar_thin_thick" || elem.type==="bar_thick_thin");
|
804
|
+
var secondthin = (elem.type==="bar_left_repeat" || elem.type==="bar_thick_thin" || elem.type==="bar_thin_thin" || elem.type==="bar_dbl_repeat");
|
805
|
+
var seconddots = (elem.type==="bar_left_repeat" || elem.type==="bar_dbl_repeat");
|
806
|
+
|
807
|
+
// limit positionning of slurs
|
808
|
+
if (firstdots || seconddots) {
|
809
|
+
for (var slur in this.slurs) {
|
810
|
+
if (this.slurs.hasOwnProperty(slur)) {
|
811
|
+
this.slurs[slur].endlimitelem = abselem;
|
812
|
+
}
|
813
|
+
}
|
814
|
+
this.startlimitelem = abselem;
|
815
|
+
}
|
816
|
+
|
817
|
+
if (firstdots) {
|
818
|
+
abselem.addRight(new ABCJS.write.RelativeElement("dots.dot", dx, 1, 7));
|
819
|
+
abselem.addRight(new ABCJS.write.RelativeElement("dots.dot", dx, 1, 5));
|
820
|
+
dx+=6; //2 hardcoded, twice;
|
821
|
+
}
|
822
|
+
|
823
|
+
if (firstthin) {
|
824
|
+
anchor = new ABCJS.write.RelativeElement(null, dx, 1, 2, {"type": "bar", "pitch2":10, linewidth:0.6});
|
825
|
+
abselem.addRight(anchor);
|
826
|
+
}
|
827
|
+
|
828
|
+
if (elem.type==="bar_invisible") {
|
829
|
+
anchor = new ABCJS.write.RelativeElement(null, dx, 1, 2, {"type": "none", "pitch2":10, linewidth:0.6});
|
830
|
+
abselem.addRight(anchor);
|
831
|
+
}
|
832
|
+
|
833
|
+
if (elem.decoration) {
|
834
|
+
this.printDecoration(elem.decoration, 12, (thick)?3:1, abselem, 0, "down", 2);
|
835
|
+
}
|
836
|
+
|
837
|
+
if (thick) {
|
838
|
+
dx+=4; //3 hardcoded;
|
839
|
+
anchor = new ABCJS.write.RelativeElement(null, dx, 4, 2, {"type": "bar", "pitch2":10, linewidth:4});
|
840
|
+
abselem.addRight(anchor);
|
841
|
+
dx+=5;
|
842
|
+
}
|
843
|
+
|
844
|
+
// if (this.partstartelem && (thick || (firstthin && secondthin))) { // means end of nth part
|
845
|
+
// this.partstartelem.anchor2=anchor;
|
846
|
+
// this.partstartelem = null;
|
847
|
+
// }
|
848
|
+
|
849
|
+
if (this.partstartelem && elem.endEnding) {
|
850
|
+
this.partstartelem.anchor2=anchor;
|
851
|
+
this.partstartelem = null;
|
852
|
+
}
|
853
|
+
|
854
|
+
if (secondthin) {
|
855
|
+
dx+=3; //3 hardcoded;
|
856
|
+
anchor = new ABCJS.write.RelativeElement(null, dx, 1, 2, {"type": "bar", "pitch2":10, linewidth:0.6});
|
857
|
+
abselem.addRight(anchor); // 3 is hardcoded
|
858
|
+
}
|
859
|
+
|
860
|
+
if (seconddots) {
|
861
|
+
dx+=3; //3 hardcoded;
|
862
|
+
abselem.addRight(new ABCJS.write.RelativeElement("dots.dot", dx, 1, 7));
|
863
|
+
abselem.addRight(new ABCJS.write.RelativeElement("dots.dot", dx, 1, 5));
|
864
|
+
} // 2 is hardcoded
|
865
|
+
|
866
|
+
if (elem.startEnding) {
|
867
|
+
this.partstartelem = new ABCJS.write.EndingElem(elem.startEnding, anchor, null);
|
868
|
+
this.voice.addOther(this.partstartelem);
|
869
|
+
}
|
870
|
+
|
871
|
+
return abselem;
|
872
|
+
|
873
|
+
};
|
874
|
+
|
875
|
+
ABCJS.write.Layout.prototype.printClef = function(elem) {
|
876
|
+
var clef = "clefs.G";
|
877
|
+
var octave = 0;
|
878
|
+
var pitch = 4;
|
879
|
+
var abselem = new ABCJS.write.AbsoluteElement(elem,0,10);
|
880
|
+
switch (elem.type) {
|
881
|
+
case "treble": break;
|
882
|
+
case "tenor": clef="clefs.C"; pitch=8; break;
|
883
|
+
case "alto": clef="clefs.C"; pitch=6; break;
|
884
|
+
case "bass": clef="clefs.F"; pitch=8; break;
|
885
|
+
case 'treble+8': octave = 1; break;
|
886
|
+
case 'tenor+8':clef="clefs.C"; pitch=8; break;
|
887
|
+
case 'bass+8': clef="clefs.F"; pitch=8; break;
|
888
|
+
case 'alto+8': clef="clefs.C"; pitch=6; break;
|
889
|
+
case 'treble-8': octave = -1; break;
|
890
|
+
case 'tenor-8':clef="clefs.C"; pitch=8; break;
|
891
|
+
case 'bass-8': clef="clefs.F"; pitch=8; break;
|
892
|
+
case 'alto-8': clef="clefs.C"; pitch=6; break;
|
893
|
+
case 'none': clef=""; break;
|
894
|
+
case 'perc': clef="clefs.perc"; pitch=6; break;
|
895
|
+
default: abselem.addChild(new ABCJS.write.RelativeElement("clef="+elem.type, 0, 0, 0, {type:"debug"}));
|
896
|
+
}
|
897
|
+
if (elem.verticalPos) {
|
898
|
+
pitch = elem.verticalPos;
|
899
|
+
}
|
900
|
+
|
901
|
+
var dx =10;
|
902
|
+
if (clef!=="") {
|
903
|
+
abselem.addRight(new ABCJS.write.RelativeElement(clef, dx, this.glyphs.getSymbolWidth(clef), pitch));
|
904
|
+
}
|
905
|
+
if (octave!==0) {
|
906
|
+
var scale= 2/3;
|
907
|
+
var adjustspacing = (this.glyphs.getSymbolWidth(clef)-this.glyphs.getSymbolWidth("8")*scale)/2;
|
908
|
+
abselem.addRight(new ABCJS.write.RelativeElement("8", dx+adjustspacing, this.glyphs.getSymbolWidth("8")*scale, (octave>0)?16:-2, {scalex:scale, scaley:scale}));
|
909
|
+
}
|
910
|
+
|
911
|
+
if (elem.stafflines===0) {
|
912
|
+
this.stafflines = 0;
|
913
|
+
} else {
|
914
|
+
this.stafflines =elem.stafflines;
|
915
|
+
}
|
916
|
+
|
917
|
+
return abselem;
|
918
|
+
};
|
919
|
+
|
920
|
+
|
921
|
+
ABCJS.write.Layout.prototype.printKeySignature = function(elem) {
|
922
|
+
var abselem = new ABCJS.write.AbsoluteElement(elem,0,10);
|
923
|
+
var dx = 0;
|
924
|
+
if (elem.accidentals) {
|
925
|
+
window.ABCJS.parse.each(elem.accidentals, function(acc) {
|
926
|
+
var symbol = (acc.acc === "sharp") ? "accidentals.sharp" : (acc.acc === "natural") ? "accidentals.nat" : "accidentals.flat";
|
927
|
+
//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 };
|
928
|
+
abselem.addRight(new ABCJS.write.RelativeElement(symbol, dx, this.glyphs.getSymbolWidth(symbol), acc.verticalPos));
|
929
|
+
dx += this.glyphs.getSymbolWidth(symbol)+2;
|
930
|
+
}, this);
|
931
|
+
}
|
932
|
+
this.startlimitelem = abselem; // limit ties here
|
933
|
+
return abselem;
|
934
|
+
};
|
935
|
+
|
936
|
+
ABCJS.write.Layout.prototype.printTimeSignature= function(elem) {
|
937
|
+
|
938
|
+
var abselem = new ABCJS.write.AbsoluteElement(elem,0,20);
|
939
|
+
if (elem.type === "specified") {
|
940
|
+
//TODO make the alignment for time signatures centered
|
941
|
+
for (var i = 0; i < elem.value.length; i++) {
|
942
|
+
if (i !== 0)
|
943
|
+
abselem.addRight(new ABCJS.write.RelativeElement('+', i*20-9, this.glyphs.getSymbolWidth("+"), 7));
|
944
|
+
if (elem.value[i].den) {
|
945
|
+
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));
|
946
|
+
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));
|
947
|
+
} else {
|
948
|
+
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));
|
949
|
+
}
|
950
|
+
}
|
951
|
+
} else if (elem.type === "common_time") {
|
952
|
+
abselem.addRight(new ABCJS.write.RelativeElement("timesig.common", 0, this.glyphs.getSymbolWidth("timesig.common"), 7));
|
953
|
+
|
954
|
+
} else if (elem.type === "cut_time") {
|
955
|
+
abselem.addRight(new ABCJS.write.RelativeElement("timesig.cut", 0, this.glyphs.getSymbolWidth("timesig.cut"), 7));
|
956
|
+
}
|
957
|
+
this.startlimitelem = abselem; // limit ties here
|
958
|
+
return abselem;
|
959
|
+
};
|