abcjs-rails 2.3 → 3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/abcjs/api/abc_animation.js +166 -4
  3. data/app/assets/javascripts/abcjs/api/abc_tunebook.js +170 -15
  4. data/app/assets/javascripts/abcjs/data/abc_tune.js +69 -47
  5. data/app/assets/javascripts/abcjs/edit/abc_editor.js +78 -37
  6. data/app/assets/javascripts/abcjs/midi/abc_midi_controls.js +513 -0
  7. data/app/assets/javascripts/abcjs/midi/abc_midi_create.js +29 -7
  8. data/app/assets/javascripts/abcjs/midi/abc_midi_flattener.js +21 -18
  9. data/app/assets/javascripts/abcjs/midi/abc_midi_js_preparer.js +233 -0
  10. data/app/assets/javascripts/abcjs/midi/abc_midi_renderer.js +3 -229
  11. data/app/assets/javascripts/abcjs/midi/abc_midi_sequencer.js +11 -3
  12. data/app/assets/javascripts/abcjs/parse/abc_common.js +24 -0
  13. data/app/assets/javascripts/abcjs/parse/abc_parse.js +58 -16
  14. data/app/assets/javascripts/abcjs/parse/abc_parse_header.js +4 -1
  15. data/app/assets/javascripts/abcjs/parse/abc_parse_key_voice.js +5 -0
  16. data/app/assets/javascripts/abcjs/write/abc_absolute_element.js +9 -2
  17. data/app/assets/javascripts/abcjs/write/abc_abstract_engraver.js +71 -19
  18. data/app/assets/javascripts/abcjs/write/abc_beam_element.js +11 -3
  19. data/app/assets/javascripts/abcjs/write/abc_brace_element.js +51 -0
  20. data/app/assets/javascripts/abcjs/write/abc_create_clef.js +3 -3
  21. data/app/assets/javascripts/abcjs/write/abc_create_key_signature.js +3 -3
  22. data/app/assets/javascripts/abcjs/write/abc_create_time_signature.js +3 -3
  23. data/app/assets/javascripts/abcjs/write/abc_crescendo_element.js +4 -1
  24. data/app/assets/javascripts/abcjs/write/abc_decoration.js +1 -1
  25. data/app/assets/javascripts/abcjs/write/abc_engraver_controller.js +10 -9
  26. data/app/assets/javascripts/abcjs/write/abc_glyphs.js +1 -1
  27. data/app/assets/javascripts/abcjs/write/abc_relative_element.js +1 -1
  28. data/app/assets/javascripts/abcjs/write/abc_renderer.js +85 -13
  29. data/app/assets/javascripts/abcjs/write/abc_staff_group_element.js +16 -0
  30. data/app/assets/javascripts/abcjs/write/abc_tempo_element.js +31 -11
  31. data/app/assets/javascripts/abcjs/write/abc_tie_element.js +8 -1
  32. data/lib/abcjs-rails/version.rb +1 -1
  33. metadata +6 -3
@@ -1,5 +1,5 @@
1
1
  // abc_midi_sequencer.js: Turn parsed abc into a linear series of events.
2
- // Copyright (C) 2010,2015 Gregory Dyke (gregdyke at gmail dot com) and Paul Rosen
2
+ // Copyright (C) 2010,2016 Gregory Dyke (gregdyke at gmail dot com) and Paul Rosen
3
3
  //
4
4
  // This program is free software: you can redistribute it and/or modify
5
5
  // it under the terms of the GNU General Public License as published by
@@ -31,9 +31,14 @@ if (!window.ABCJS.midi)
31
31
  // Global options
32
32
  options = options || {};
33
33
  var qpm = options.qpm || 180; // The tempo if there isn't a tempo specified.
34
- var program = options.program || 2; // The program if there isn't a program specified.
34
+ var program = options.program || 0; // The program if there isn't a program specified.
35
35
  var transpose = options.transpose || 0;
36
36
  var channel = options.channel || 0;
37
+ // All of the above overrides need to be integers
38
+ qpm = parseInt(qpm, 10);
39
+ program = parseInt(program, 10);
40
+ transpose = parseInt(transpose, 10);
41
+ channel = parseInt(channel, 10);
37
42
 
38
43
  var bagpipes = abctune.formatting.bagpipes; // If it is bagpipes, then the gracenotes are played on top of the main note.
39
44
  if (bagpipes)
@@ -142,7 +147,7 @@ if (!window.ABCJS.midi)
142
147
  // figure out repeats and endings --
143
148
  // The important part is where there is a start repeat, and end repeat, or a first ending.
144
149
  var endRepeat = (elem.type === "bar_right_repeat" || elem.type === "bar_dbl_repeat");
145
- var startEnding = (elem.startEnding) ? true : false;
150
+ var startEnding = (elem.startEnding === '1');
146
151
  var startRepeat = (elem.type === "bar_left_repeat" || elem.type === "bar_dbl_repeat" || elem.type === "bar_thick_thin" || elem.type === "bar_thin_thick" || elem.type === "bar_thin_thin" || elem.type === "bar_right_repeat");
147
152
  if (endRepeat) {
148
153
  var s = startRepeatPlaceholder[voiceNumber];
@@ -150,6 +155,9 @@ if (!window.ABCJS.midi)
150
155
  var e = skipEndingPlaceholder[voiceNumber];
151
156
  if (!e) e = voices[voiceNumber].length; // If there wasn't a first ending marker, then we copy everything.
152
157
  voices[voiceNumber] = voices[voiceNumber].concat(voices[voiceNumber].slice(s, e));
158
+ // reset these in case there is a second repeat later on.
159
+ skipEndingPlaceholder[voiceNumber] = undefined;
160
+ startRepeatPlaceholder[voiceNumber] = undefined;
153
161
  }
154
162
  if (startEnding)
155
163
  skipEndingPlaceholder[voiceNumber] = voices[voiceNumber].length;
@@ -30,6 +30,30 @@ window.ABCJS.parse.clone = function(source) {
30
30
  return destination;
31
31
  };
32
32
 
33
+ window.ABCJS.parse.cloneArray = function(source) {
34
+ var destination = [];
35
+ for (var i = 0; i < source.length; i++) {
36
+ destination.push(ABCJS.parse.clone(source[i]));
37
+ }
38
+ return destination;
39
+ };
40
+
41
+ window.ABCJS.parse.cloneHashOfHash = function(source) {
42
+ var destination = {};
43
+ for (var property in source)
44
+ if (source.hasOwnProperty(property))
45
+ destination[property] = ABCJS.parse.clone(source[property]);
46
+ return destination;
47
+ };
48
+
49
+ window.ABCJS.parse.cloneHashOfArrayOfHash = function(source) {
50
+ var destination = {};
51
+ for (var property in source)
52
+ if (source.hasOwnProperty(property))
53
+ destination[property] = ABCJS.parse.cloneArray(source[property]);
54
+ return destination;
55
+ };
56
+
33
57
  window.ABCJS.parse.gsub = function(source, pattern, replacement) {
34
58
  return source.split(pattern).join(replacement);
35
59
  };
@@ -840,14 +840,12 @@ window.ABCJS.parse.Parse = function() {
840
840
  if (multilineVars.currentVoice.style)
841
841
  params.style = multilineVars.currentVoice.style;
842
842
  }
843
+ var isFirstVoice = multilineVars.currentVoice === undefined || (multilineVars.currentVoice.staffNum === 0 && multilineVars.currentVoice.index === 0);
844
+ if (multilineVars.barNumbers === 0 && isFirstVoice && multilineVars.currBarNumber !== 1)
845
+ params.barNumber = multilineVars.currBarNumber;
843
846
  tune.startNewLine(params);
844
847
 
845
848
  multilineVars.partForNextLine = "";
846
- if (multilineVars.currentVoice === undefined || (multilineVars.currentVoice.staffNum === multilineVars.staves.length-1 && multilineVars.staves[multilineVars.currentVoice.staffNum].numVoices-1 === multilineVars.currentVoice.index)) {
847
- //multilineVars.meter = null;
848
- if (multilineVars.barNumbers === 0)
849
- multilineVars.barNumOnNextNote = multilineVars.currBarNumber;
850
- }
851
849
  }
852
850
 
853
851
  var letter_to_grace = function(line, i) {
@@ -1145,9 +1143,12 @@ window.ABCJS.parse.Parse = function() {
1145
1143
  else if (bar.endEnding)
1146
1144
  multilineVars.barFirstEndingNum = undefined;
1147
1145
  if (bar.type !== 'bar_invisible' && multilineVars.measureNotEmpty) {
1148
- multilineVars.currBarNumber++;
1149
- if (multilineVars.barNumbers && multilineVars.currBarNumber % multilineVars.barNumbers === 0)
1150
- multilineVars.barNumOnNextNote = multilineVars.currBarNumber;
1146
+ var isFirstVoice = multilineVars.currentVoice === undefined || (multilineVars.currentVoice.staffNum === 0 && multilineVars.currentVoice.index === 0);
1147
+ if (isFirstVoice) {
1148
+ multilineVars.currBarNumber++;
1149
+ if (multilineVars.barNumbers && multilineVars.currBarNumber % multilineVars.barNumbers === 0)
1150
+ bar.barNumber = multilineVars.currBarNumber;
1151
+ }
1151
1152
  }
1152
1153
  multilineVars.addFormattingOptions(el, tune.formatting, 'bar');
1153
1154
  tune.appendElement('bar', startOfLine+i, startOfLine+i+ret[0], bar);
@@ -1291,10 +1292,7 @@ window.ABCJS.parse.Parse = function() {
1291
1292
  if (chordDuration !== null) {
1292
1293
  el.duration = el.duration * chordDuration;
1293
1294
  }
1294
- if (multilineVars.barNumOnNextNote) {
1295
- el.barNumber = multilineVars.barNumOnNextNote;
1296
- multilineVars.barNumOnNextNote = null;
1297
- }
1295
+
1298
1296
  multilineVars.addFormattingOptions(el, tune.formatting, 'note');
1299
1297
  tune.appendElement('note', startOfLine+chordStartChar, startOfLine+i, el);
1300
1298
  multilineVars.measureNotEmpty = true;
@@ -1365,10 +1363,6 @@ window.ABCJS.parse.Parse = function() {
1365
1363
  el.duration = durationOfMeasure(multilineVars);
1366
1364
  }
1367
1365
 
1368
- if (multilineVars.barNumOnNextNote) {
1369
- el.barNumber = multilineVars.barNumOnNextNote;
1370
- multilineVars.barNumOnNextNote = null;
1371
- }
1372
1366
  multilineVars.addFormattingOptions(el, tune.formatting, 'note');
1373
1367
  tune.appendElement('note', startOfLine+startI, startOfLine+i, el);
1374
1368
  multilineVars.measureNotEmpty = true;
@@ -1400,12 +1394,57 @@ window.ABCJS.parse.Parse = function() {
1400
1394
  parseLine(ret.str);
1401
1395
  };
1402
1396
 
1397
+ function appendLastMeasure(voice, nextVoice) {
1398
+ voice.push({
1399
+ el_type: 'hint'
1400
+ });
1401
+ for (var i = 0; i < nextVoice.length; i++) {
1402
+ var element = nextVoice[i];
1403
+ var hint = window.ABCJS.parse.clone(element);
1404
+ voice.push(hint);
1405
+ if (element.el_type === 'bar')
1406
+ return;
1407
+ }
1408
+ }
1409
+
1410
+ function addHintMeasure(staff, nextStaff) {
1411
+ for (var i = 0; i < staff.length; i++) {
1412
+ var stave = staff[i];
1413
+ var nextStave = nextStaff[i];
1414
+ if (nextStave) { // Be sure there is the same number of staves on the next line.
1415
+ for (var j = 0; j < nextStave.voices.length; j++) {
1416
+ var nextVoice = nextStave.voices[j];
1417
+ var voice = stave.voices[j];
1418
+ if (voice) { // Be sure there are the same number of voices on the previous line.
1419
+ appendLastMeasure(voice, nextVoice);
1420
+ }
1421
+ }
1422
+ }
1423
+ }
1424
+ }
1425
+
1426
+ function addHintMeasures() {
1427
+ for (var i = 0; i < tune.lines.length; i++) {
1428
+ var line = tune.lines[i].staff;
1429
+ if (line) {
1430
+ var j = i+1;
1431
+ while (j < tune.lines.length && tune.lines[j].staff === undefined)
1432
+ j++;
1433
+ if (j < tune.lines.length) {
1434
+ var nextLine = tune.lines[j].staff;
1435
+ addHintMeasure(line, nextLine);
1436
+ }
1437
+ }
1438
+ }
1439
+ }
1440
+
1403
1441
  this.parse = function(strTune, switches) {
1404
1442
  // the switches are optional and cause a difference in the way the tune is parsed.
1405
1443
  // switches.header_only : stop parsing when the header is finished
1406
1444
  // switches.stop_on_warning : stop at the first warning encountered.
1407
1445
  // switches.print: format for the page instead of the browser.
1408
1446
  // switches.format: a hash of the desired formatting commands.
1447
+ // switches.hint_measures: put the next measure at the end of the current line.
1409
1448
  if (!switches) switches = {};
1410
1449
  tune.reset();
1411
1450
  if (switches.print)
@@ -1482,5 +1521,8 @@ window.ABCJS.parse.Parse = function() {
1482
1521
  if (err !== "normal_abort")
1483
1522
  throw err;
1484
1523
  }
1524
+ if (switches.hint_measures) {
1525
+ addHintMeasures();
1526
+ }
1485
1527
  };
1486
1528
  };
@@ -34,7 +34,10 @@ window.ABCJS.parse.ParseHeader = function(tokenizer, warn, multilineVars, tune)
34
34
  tune.addSubtitle(tokenizer.translateString(tokenizer.stripComment(title))); // display secondary title
35
35
  else
36
36
  {
37
- tune.addMetaText("title", tokenizer.translateString(tokenizer.theReverser(tokenizer.stripComment(title))));
37
+ var titleStr = tokenizer.translateString(tokenizer.theReverser(tokenizer.stripComment(title)));
38
+ if (multilineVars.titlecaps)
39
+ titleStr = titleStr.toUpperCase();
40
+ tune.addMetaText("title", titleStr);
38
41
  multilineVars.hasMainTitle = true;
39
42
  }
40
43
  };
@@ -403,6 +403,11 @@ window.ABCJS.parse.parseKeyVoice = {};
403
403
  mode = retMode.token;
404
404
  }
405
405
  }
406
+ // Be sure that the key specified is in the list: not all keys are physically possible, like Cbmin.
407
+ if (window.ABCJS.parse.parseKeyVoice.standardKey(key) === undefined) {
408
+ warn("Unsupported key signature: " + key, str, 0);
409
+ return ret;
410
+ }
406
411
  }
407
412
  // We need to do a deep copy because we are going to modify it
408
413
  var oldKey = window.ABCJS.parse.parseKeyVoice.deepCopyKey(multilineVars.key);
@@ -25,8 +25,9 @@ if (!window.ABCJS.write)
25
25
  // duration - actual musical duration - different from notehead duration in triplets. refer to abcelem to get the notehead duration
26
26
  // minspacing - spacing which must be taken on top of the width defined by the duration
27
27
  // type is a meta-type for the element. It is not necessary for drawing, but it is useful to make semantic sense of the element. For instance, it can be used in the element's class name.
28
- ABCJS.write.AbsoluteElement = function(abcelem, duration, minspacing, type) {
28
+ ABCJS.write.AbsoluteElement = function(abcelem, duration, minspacing, type, tuneNumber) {
29
29
  //console.log("Absolute:",abcelem, type);
30
+ this.tuneNumber = tuneNumber;
30
31
  this.abcelem = abcelem;
31
32
  this.duration = duration;
32
33
  this.minspacing = minspacing || 0;
@@ -165,6 +166,10 @@ ABCJS.write.AbsoluteElement.prototype.setX = function (x) {
165
166
  this.children[i].setX(x);
166
167
  };
167
168
 
169
+ ABCJS.write.AbsoluteElement.prototype.setHint = function () {
170
+ this.hint = true;
171
+ };
172
+
168
173
  ABCJS.write.AbsoluteElement.prototype.draw = function (renderer, bartop) {
169
174
  this.elemset = renderer.paper.set();
170
175
  if (this.invisible) return;
@@ -179,13 +184,15 @@ ABCJS.write.AbsoluteElement.prototype.draw = function (renderer, bartop) {
179
184
  this.elemset.push(renderer.endGroup(this.type));
180
185
  if (this.klass)
181
186
  this.setClass("mark", "", "#00ff00");
187
+ if (this.hint)
188
+ this.setClass("abcjs-hint", "", null);
182
189
  var color = ABCJS.write.debugPlacement ? "rgba(0,0,0,0.3)" : "rgba(0,0,0,0)"; // Create transparent box that encompasses the element, and not so transparent to debug it.
183
190
  var target = renderer.printShadedBox(this.x, renderer.calcY(this.top), this.w, renderer.calcY(this.bottom)-renderer.calcY(this.top), color);
184
191
  var self = this;
185
192
  var controller = renderer.controller;
186
193
  // this.elemset.mouseup(function () {
187
194
  target.mouseup(function () {
188
- controller.notifySelect(self);
195
+ controller.notifySelect(self, self.tuneNumber);
189
196
  });
190
197
  this.abcelem.abselem = this;
191
198
 
@@ -43,9 +43,10 @@ ABCJS.write.getDurlog = function(duration) {
43
43
  return Math.floor(Math.log(duration)/Math.log(2));
44
44
  };
45
45
 
46
- ABCJS.write.AbstractEngraver = function(bagpipes, renderer) {
46
+ ABCJS.write.AbstractEngraver = function(bagpipes, renderer, tuneNumber) {
47
47
  this.decoration = new ABCJS.write.Decoration();
48
48
  this.renderer = renderer;
49
+ this.tuneNumber = tuneNumber;
49
50
  this.isBagpipes = bagpipes;
50
51
  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"},
51
52
  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", 7:"noteheads.quarter", 'nostem':"noteheads.quarter"},
@@ -138,9 +139,11 @@ ABCJS.write.AbstractEngraver.prototype.createABCLine = function(staffs, tempo) {
138
139
  this.staffgroup = new ABCJS.write.StaffGroupElement();
139
140
  this.tempoSet = false;
140
141
  for (this.s = 0; this.s < staffs.length; this.s++) {
142
+ if (ABCJS.write.hint)
143
+ this.restoreState();
144
+ ABCJS.write.hint = false;
141
145
  this.createABCStaff(staffs[this.s], tempo);
142
146
  }
143
-
144
147
  return this.staffgroup;
145
148
  };
146
149
 
@@ -155,16 +158,20 @@ ABCJS.write.AbstractEngraver.prototype.createABCStaff = function(abcstaff, tempo
155
158
  this.voice.duplicate = true; // bar lines and other duplicate info need not be created
156
159
  }
157
160
  if (abcstaff.title && abcstaff.title[this.v]) this.voice.header=abcstaff.title[this.v];
158
- var clef = ABCJS.write.createClef(abcstaff.clef);
159
- if (clef)
160
- this.voice.addChild(clef);
161
- var keySig = ABCJS.write.createKeySignature(abcstaff.key);
161
+ var clef = ABCJS.write.createClef(abcstaff.clef, this.tuneNumber);
162
+ if (clef) {
163
+ if (this.v ===0 && abcstaff.barNumber) {
164
+ this.addMeasureNumber(abcstaff.barNumber, clef);
165
+ }
166
+ this.voice.addChild(clef);
167
+ }
168
+ var keySig = ABCJS.write.createKeySignature(abcstaff.key, this.tuneNumber);
162
169
  if (keySig) {
163
170
  this.voice.addChild(keySig);
164
171
  this.startlimitelem = keySig; // limit ties here
165
172
  }
166
173
  if (abcstaff.meter) {
167
- var ts = ABCJS.write.createTimeSignature(abcstaff.meter);
174
+ var ts = ABCJS.write.createTimeSignature(abcstaff.meter, this.tuneNumber);
168
175
  this.voice.addChild(ts);
169
176
  this.startlimitelem = ts; // limit ties here
170
177
  }
@@ -174,6 +181,18 @@ ABCJS.write.AbstractEngraver.prototype.createABCStaff = function(abcstaff, tempo
174
181
  this.staffgroup.addVoice(this.voice,this.s,staffLines);
175
182
  this.createABCVoice(abcstaff.voices[this.v],tempo);
176
183
  this.staffgroup.setStaffLimits(this.voice);
184
+ //Tony: Here I am following what staves need to be surrounded by the brace, by incrementing the length of the brace class.
185
+ //So basically this keeps incrementing the number of staff surrounded by the brace until it sees "end".
186
+ //This then gets processed in abc_staff_group_element.js, so that it will have the correct top and bottom coordinates for the brace.
187
+ if(abcstaff.brace === "start"){
188
+ this.staffgroup.brace = new ABCJS.write.BraceElem(1, true);
189
+ }
190
+ else if(abcstaff.brace === "end" && this.staffgroup.brace) {
191
+ this.staffgroup.brace.increaseStavesIncluded();
192
+ }
193
+ else if(abcstaff.brace === "continue" && this.staffgroup.brace){
194
+ this.staffgroup.brace.increaseStavesIncluded();
195
+ }
177
196
  }
178
197
  };
179
198
 
@@ -188,11 +207,13 @@ ABCJS.write.AbstractEngraver.prototype.createABCVoice = function(abcline, tempo)
188
207
  for (var slur in this.slurs) {
189
208
  if (this.slurs.hasOwnProperty(slur)) {
190
209
  this.slurs[slur]= new ABCJS.write.TieElem(null, null, this.slurs[slur].above, this.slurs[slur].force, false);
210
+ if (ABCJS.write.hint) this.slurs[slur].setHint();
191
211
  this.voice.addOther(this.slurs[slur]);
192
212
  }
193
213
  }
194
214
  for (var i=0; i<this.ties.length; i++) {
195
215
  this.ties[i]=new ABCJS.write.TieElem(null, null, this.ties[i].above, this.ties[i].force, true);
216
+ if (ABCJS.write.hint) this.ties[i].setHint();
196
217
  this.voice.addOther(this.ties[i]);
197
218
  }
198
219
 
@@ -202,7 +223,7 @@ ABCJS.write.AbstractEngraver.prototype.createABCVoice = function(abcline, tempo)
202
223
  for (i=0; i<abselems.length; i++) {
203
224
  if (!this.tempoSet && tempo && !tempo.suppress) {
204
225
  this.tempoSet = true;
205
- abselems[i].addChild(new ABCJS.write.TempoElement(tempo));
226
+ abselems[i].addChild(new ABCJS.write.TempoElement(tempo, this.tuneNumber));
206
227
  }
207
228
  this.voice.addChild(abselems[i]);
208
229
  }
@@ -211,6 +232,19 @@ ABCJS.write.AbstractEngraver.prototype.createABCVoice = function(abcline, tempo)
211
232
  this.pushCrossLineElems();
212
233
  };
213
234
 
235
+ ABCJS.write.AbstractEngraver.prototype.saveState = function() {
236
+ this.tiesSave = ABCJS.parse.cloneArray(this.ties);
237
+ this.slursSave = ABCJS.parse.cloneHashOfHash(this.slurs);
238
+ this.slursbyvoiceSave = ABCJS.parse.cloneHashOfHash(this.slursbyvoice);
239
+ this.tiesbyvoiceSave = ABCJS.parse.cloneHashOfArrayOfHash(this.tiesbyvoice);
240
+ };
241
+
242
+ ABCJS.write.AbstractEngraver.prototype.restoreState = function() {
243
+ this.ties = ABCJS.parse.cloneArray(this.tiesSave);
244
+ this.slurs = ABCJS.parse.cloneHashOfHash(this.slursSave);
245
+ this.slursbyvoice = ABCJS.parse.cloneHashOfHash(this.slursbyvoiceSave);
246
+ this.tiesbyvoice = ABCJS.parse.cloneHashOfArrayOfHash(this.tiesbyvoiceSave);
247
+ };
214
248
 
215
249
  // return an array of ABCJS.write.AbsoluteElement
216
250
  ABCJS.write.AbstractEngraver.prototype.createABCElement = function() {
@@ -225,17 +259,17 @@ ABCJS.write.AbstractEngraver.prototype.createABCElement = function() {
225
259
  if (this.voice.duplicate) elemset[0].invisible = true;
226
260
  break;
227
261
  case "meter":
228
- elemset[0] = ABCJS.write.createTimeSignature(elem);
262
+ elemset[0] = ABCJS.write.createTimeSignature(elem, this.tuneNumber);
229
263
  this.startlimitelem = elemset[0]; // limit ties here
230
264
  if (this.voice.duplicate) elemset[0].invisible = true;
231
265
  break;
232
266
  case "clef":
233
- elemset[0] = ABCJS.write.createClef(elem);
267
+ elemset[0] = ABCJS.write.createClef(elem, this.tuneNumber);
234
268
  if (!elemset[0]) return null;
235
269
  if (this.voice.duplicate) elemset[0].invisible = true;
236
270
  break;
237
271
  case "key":
238
- var absKey = ABCJS.write.createKeySignature(elem);
272
+ var absKey = ABCJS.write.createKeySignature(elem, this.tuneNumber);
239
273
  if (absKey) {
240
274
  elemset[0] = absKey;
241
275
  this.startlimitelem = elemset[0]; // limit ties here
@@ -246,14 +280,14 @@ ABCJS.write.AbstractEngraver.prototype.createABCElement = function() {
246
280
  this.stemdir=elem.direction;
247
281
  break;
248
282
  case "part":
249
- var abselem = new ABCJS.write.AbsoluteElement(elem,0,0, 'part');
283
+ var abselem = new ABCJS.write.AbsoluteElement(elem,0,0, 'part', this.tuneNumber);
250
284
  var dim = this.renderer.getTextSize(elem.title, 'partsfont', "part");
251
285
  abselem.addChild(new ABCJS.write.RelativeElement(elem.title, 0, 0, undefined, {type:"part", height: dim.height/ABCJS.write.spacing.STEP}));
252
286
  elemset[0] = abselem;
253
287
  break;
254
288
  case "tempo":
255
- var abselem3 = new ABCJS.write.AbsoluteElement(elem,0,0, 'tempo');
256
- abselem3.addChild(new ABCJS.write.TempoElement(elem));
289
+ var abselem3 = new ABCJS.write.AbsoluteElement(elem,0,0, 'tempo', this.tuneNumber);
290
+ abselem3.addChild(new ABCJS.write.TempoElement(elem, this.tuneNumber));
257
291
  elemset[0] = abselem3;
258
292
  break;
259
293
  case "style":
@@ -262,8 +296,12 @@ ABCJS.write.AbstractEngraver.prototype.createABCElement = function() {
262
296
  else
263
297
  this.style = elem.head;
264
298
  break;
299
+ case "hint":
300
+ ABCJS.write.hint = true;
301
+ this.saveState();
302
+ break;
265
303
  default:
266
- var abselem2 = new ABCJS.write.AbsoluteElement(elem,0,0, 'unsupported');
304
+ var abselem2 = new ABCJS.write.AbsoluteElement(elem,0,0, 'unsupported', this.tuneNumber);
267
305
  abselem2.addChild(new ABCJS.write.RelativeElement("element type "+elem.el_type, 0, 0, undefined, {type:"debug"}));
268
306
  elemset[0] = abselem2;
269
307
  }
@@ -296,6 +334,7 @@ ABCJS.write.AbstractEngraver.prototype.createBeam = function() {
296
334
  if (this.getElem().startBeam && !this.getElem().endBeam) {
297
335
  var dir = this.calcBeamDir();
298
336
  var beamelem = new ABCJS.write.BeamElem(this.stemHeight, dir);
337
+ if (ABCJS.write.hint) beamelem.setHint();
299
338
  var oldDir = this.stemdir;
300
339
  this.stemdir = dir;
301
340
  while (this.getElem()) {
@@ -360,8 +399,8 @@ ABCJS.write.AbstractEngraver.prototype.createNote = function(elem, nostem, dontD
360
399
  }
361
400
 
362
401
 
363
- var abselem = new ABCJS.write.AbsoluteElement(elem, duration * this.tripletmultiplier, 1, 'note');
364
-
402
+ var abselem = new ABCJS.write.AbsoluteElement(elem, duration * this.tripletmultiplier, 1, 'note', this.tuneNumber);
403
+ if (ABCJS.write.hint) abselem.setHint();
365
404
 
366
405
  if (elem.rest) {
367
406
  var restpitch = 7;
@@ -542,6 +581,7 @@ ABCJS.write.AbstractEngraver.prototype.createNote = function(elem, nostem, dontD
542
581
  var gracebeam = null;
543
582
  if (elem.gracenotes.length>1) {
544
583
  gracebeam = new ABCJS.write.BeamElem(this.stemHeight*graceScaleStem, "grace",this.isBagpipes);
584
+ if (ABCJS.write.hint) gracebeam.setHint();
545
585
  gracebeam.mainNote = abselem; // this gives us a reference back to the note this is attached to so that the stems can be attached somewhere.
546
586
  }
547
587
 
@@ -774,7 +814,9 @@ ABCJS.write.AbstractEngraver.prototype.createNoteHead = function(abselem, c, pit
774
814
  if (pitchelem.startTie) {
775
815
  //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"));
776
816
  var tie = new ABCJS.write.TieElem(notehead, null, (this.stemdir==="down" || dir==="down") && this.stemdir!=="up",(this.stemdir==="down" || this.stemdir==="up"), true);
777
- this.ties[this.ties.length]=tie;
817
+ if (ABCJS.write.hint) tie.setHint();
818
+
819
+ this.ties[this.ties.length]=tie;
778
820
  this.voice.addOther(tie);
779
821
  // HACK-PER: For the animation, we need to know if a note is tied to the next one, so here's a flag.
780
822
  // Unfortunately, only some of the notes in the current event might be tied, but this will consider it
@@ -792,6 +834,7 @@ ABCJS.write.AbstractEngraver.prototype.createNoteHead = function(abselem, c, pit
792
834
  delete this.slurs[slurid];
793
835
  } else {
794
836
  slur = new ABCJS.write.TieElem(null, notehead, dir==="down",(this.stemdir==="up" || dir==="down") && this.stemdir!=="down", false);
837
+ if (ABCJS.write.hint) slur.setHint();
795
838
  this.voice.addOther(slur);
796
839
  }
797
840
  if (this.startlimitelem) {
@@ -805,6 +848,7 @@ ABCJS.write.AbstractEngraver.prototype.createNoteHead = function(abselem, c, pit
805
848
  var slurid = pitchelem.startSlur[i].label;
806
849
  //PER: bug fix: var slur = new ABCJS.write.TieElem(notehead, null, (this.stemdir=="up" || dir=="down") && this.stemdir!="down", this.stemdir);
807
850
  var slur = new ABCJS.write.TieElem(notehead, null, (this.stemdir==="down" || dir==="down") && this.stemdir!=="up", false, false);
851
+ if (ABCJS.write.hint) slur.setHint();
808
852
  this.slurs[slurid]=slur;
809
853
  this.voice.addOther(slur);
810
854
  }
@@ -814,13 +858,21 @@ ABCJS.write.AbstractEngraver.prototype.createNoteHead = function(abselem, c, pit
814
858
 
815
859
  };
816
860
 
861
+ ABCJS.write.AbstractEngraver.prototype.addMeasureNumber = function (number, abselem) {
862
+ var measureNumHeight = this.renderer.getTextSize(number, "measurefont", 'bar-number');
863
+ abselem.addChild(new ABCJS.write.RelativeElement(number, 0, 0, 11+measureNumHeight.height / ABCJS.write.spacing.STEP, {type:"barNumber"}));
864
+ };
865
+
817
866
  ABCJS.write.AbstractEngraver.prototype.createBarLine = function (elem) {
818
867
  // bar_thin, bar_thin_thick, bar_thin_thin, bar_thick_thin, bar_right_repeat, bar_left_repeat, bar_double_repeat
819
868
 
820
- var abselem = new ABCJS.write.AbsoluteElement(elem, 0, 10, 'bar');
869
+ var abselem = new ABCJS.write.AbsoluteElement(elem, 0, 10, 'bar', this.tuneNumber);
821
870
  var anchor = null; // place to attach part lines
822
871
  var dx = 0;
823
872
 
873
+ if (elem.barNumber) {
874
+ this.addMeasureNumber(elem.barNumber, abselem);
875
+ }
824
876
 
825
877
 
826
878
  var firstdots = (elem.type==="bar_right_repeat" || elem.type==="bar_dbl_repeat");