abcjs 6.4.4 → 6.5.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abcjs",
3
- "version": "6.4.4",
3
+ "version": "6.5.1",
4
4
  "description": "Renderer for abc music notation",
5
5
  "main": "index.js",
6
6
  "types": "types/index.d.ts",
@@ -938,14 +938,27 @@ var parseDirective = {};
938
938
  }
939
939
  multilineVars.currBarNumber = tuneBuilder.setBarNumberImmediate(tokens[0].intt);
940
940
  break;
941
+ case "keywarn":
942
+ if (tokens.length !== 1 || tokens[0].type !== 'number' || (tokens[0].intt !== 1 && tokens[0].intt !== 0)) {
943
+ return 'Directive ' + cmd + ' requires 0 or 1 as a parameter.';
944
+ }
945
+ multilineVars[cmd] = tokens[0].intt === 1
946
+ break;
941
947
  case "begintext":
942
948
  var textBlock = '';
943
949
  line = tokenizer.nextLine();
944
950
  while(line && line.indexOf('%%endtext') !== 0) {
945
- if (parseCommon.startsWith(line, "%%"))
946
- textBlock += line.substring(2) + "\n";
947
- else
948
- textBlock += line + "\n";
951
+ // MAE 9 May 2025 - for text blocks with just white space
952
+ if (parseCommon.startsWith(line, "%%")){
953
+
954
+ var theLine = line.substring(2);
955
+ theLine = theLine.trim() + "\n";
956
+ textBlock += theLine;
957
+
958
+ }
959
+ else{
960
+ textBlock += line.trim() + "\n";
961
+ }
949
962
  line = tokenizer.nextLine();
950
963
  }
951
964
  tuneBuilder.addText(textBlock, { startChar: multilineVars.iChar, endChar: multilineVars.iChar+textBlock.length+7});
@@ -1119,6 +1132,17 @@ var parseDirective = {};
1119
1132
  }
1120
1133
  break;
1121
1134
 
1135
+ case "maxstaves":
1136
+ var nStaves = tokenizer.getInt(restOfString)
1137
+ if (nStaves.digits === 0)
1138
+ warn("Expected number of staves in maxstaves")
1139
+ else{
1140
+ if (nStaves.value > 0){
1141
+ tune.formatting.maxStaves = nStaves.value;
1142
+ }
1143
+ }
1144
+ break;
1145
+
1122
1146
  case "newpage":
1123
1147
  var pgNum = tokenizer.getInt(restOfString);
1124
1148
  tuneBuilder.addNewPage(pgNum.digits === 0 ? -1 : pgNum.value);
@@ -1179,6 +1203,14 @@ var parseDirective = {};
1179
1203
  }
1180
1204
  break;
1181
1205
 
1206
+ case "visualtranspose":
1207
+ var halfSteps = tokenizer.getInt(restOfString)
1208
+ if (halfSteps.digits === 0)
1209
+ warn("Expected number of half steps in visualTranspose")
1210
+ else
1211
+ multilineVars.globalTranspose = halfSteps.value
1212
+ break;
1213
+
1182
1214
  case "map":
1183
1215
  case "playtempo":
1184
1216
  case "auquality":
@@ -429,9 +429,9 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
429
429
  return [ line.length ];
430
430
  case "K:":
431
431
  var result = parseKeyVoice.parseKey(line.substring(i+2), tuneBuilder.hasBeginMusic());
432
- if (result.foundClef && tuneBuilder.hasBeginMusic())
432
+ if (result.foundClef && tuneBuilder.hasBeginMusic() && multilineVars.keywarn !== false)
433
433
  tuneBuilder.appendStartingElement('clef', multilineVars.iChar + i, multilineVars.iChar + line.length, multilineVars.clef);
434
- if (result.foundKey && tuneBuilder.hasBeginMusic())
434
+ if (result.foundKey && tuneBuilder.hasBeginMusic() && multilineVars.keywarn !== false)
435
435
  tuneBuilder.appendStartingElement('key', multilineVars.iChar + i, multilineVars.iChar + line.length, parseKeyVoice.fixKey(multilineVars.clef, multilineVars.key));
436
436
  return [ line.length ];
437
437
  case "P:":
@@ -504,7 +504,7 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
504
504
  // since the key is the last thing that can happen in the header, we can resolve the tempo now
505
505
  this.resolveTempo();
506
506
  var result = parseKeyVoice.parseKey(line.substring(2), false);
507
- if (!multilineVars.is_in_header && tuneBuilder.hasBeginMusic()) {
507
+ if (!multilineVars.is_in_header && tuneBuilder.hasBeginMusic() && multilineVars.keywarn !== false) {
508
508
  if (result.foundClef)
509
509
  tuneBuilder.appendStartingElement('clef', startChar, endChar, multilineVars.clef);
510
510
  if (result.foundKey)
@@ -292,7 +292,7 @@ var parseKeyVoice = {};
292
292
  if (isInline)
293
293
  multilineVars.globalTransposeOrigKeySig = savedOrigKey
294
294
  multilineVars.key.mode = mode;
295
- if (oldKey) {
295
+ if (oldKey && multilineVars.keywarn !== false) {
296
296
  // Add natural in all places that the old key had an accidental.
297
297
  var kk;
298
298
  for (var k = 0; k < multilineVars.key.accidentals.length; k++) {
@@ -644,13 +644,48 @@ var parseKeyVoice = {};
644
644
  case 'tenor,,':
645
645
  case 'alto,,':
646
646
  case 'none,,':
647
+ // MAE 26 May 2025 Start of additional clefs
648
+ case 'treble+8':
649
+ case 'treble-8':
650
+ case 'treble^8':
651
+ case 'treble_8':
652
+ case 'treble1':
653
+ case 'treble2':
654
+ case 'treble3':
655
+ case 'treble4':
656
+ case 'treble5':
657
+ case 'bass+8':
658
+ case 'bass-8':
659
+ case 'bass^8':
660
+ case 'bass_8':
661
+ case 'bass+16':
662
+ case 'bass-16':
663
+ case 'bass^16':
664
+ case 'bass_16':
665
+ case 'bass1':
666
+ case 'bass2':
667
+ case 'bass3':
668
+ case 'bass4':
669
+ case 'bass5':
670
+ case 'tenor1':
671
+ case 'tenor2':
672
+ case 'tenor3':
673
+ case 'tenor4':
674
+ case 'tenor5':
675
+ case 'alto1':
676
+ case 'alto2':
677
+ case 'alto3':
678
+ case 'alto4':
679
+ case 'alto5':
680
+ case 'alto+8':
681
+ case 'alto-8':
682
+ case 'alto^8':
683
+ case 'alto_8':
684
+ // MAE 26 May 2025 End of additional clefs
685
+
647
686
  // TODO-PER: handle the octave indicators on the clef by changing the middle property
648
687
  var oct2 = 0;
649
- // for (var iii = 0; iii < token.token.length; iii++) {
650
- // if (token.token[iii] === ',') oct2 -= 7;
651
- // else if (token.token[iii] === "'") oct2 += 7;
652
- // }
653
- staffInfo.clef = token.token.replace(/[',]/g, ""); //'//comment for emacs formatting of regexp
688
+ staffInfo.clef = token.token.replace(/[',]/g, ""); //'//comment for emacs formatting of regexp
654
689
  staffInfo.verticalPos = calcMiddle(staffInfo.clef, oct2);
655
690
  multilineVars.voices[id].clef = token.token;
656
691
  break;
@@ -835,6 +835,8 @@ var letter_to_accent = function(line, i) {
835
835
  case 'R':return [1, 'roll'];
836
836
  case 'S':return [1, 'segno'];
837
837
  case 'T':return [1, 'trill'];
838
+ case 't':return [1, 'trillh'];
839
+
838
840
  }
839
841
  return [0, 0];
840
842
  };
@@ -1,5 +1,6 @@
1
1
  module.exports.legalAccents = [
2
2
  'trill',
3
+ 'trillh',
3
4
  'lowermordent',
4
5
  'uppermordent',
5
6
  'mordent',
@@ -677,9 +677,17 @@ function getTitleNumber(str){
677
677
  }
678
678
 
679
679
  var thePatterns = [
680
- { match: /,\s*[Tt]he$/, replace: "The " },
681
- { match: /,\s*[Aa]$/, replace: "A " },
682
- { match: /,\s*[Aa]n$/, replace: "An " },
680
+ { match: /,\s*The$/, replace: "The " },
681
+ { match: /,\s*the$/, replace: "the " },
682
+ { match: /,\s*A$/, replace: "A " },
683
+ { match: /,\s*a$/, replace: "a " },
684
+ { match: /,\s*An$/, replace: "An " },
685
+ { match: /,\s*an$/, replace: "an " },
686
+ { match: /,\s*Da$/, replace: "Da " },
687
+ { match: /,\s*La$/, replace: "La " },
688
+ { match: /,\s*Le$/, replace: "Le " },
689
+ { match: /,\s*Les$/, replace: "Les " },
690
+ { match: /,\s*Ye$/, replace: "Ye " },
683
691
  ]
684
692
 
685
693
  this.theReverser = function (str) {
@@ -65,7 +65,7 @@ transpose.keySignature = function(multilineVars, keyName, root, acc, localTransp
65
65
  var newKeyName = (keyName[0] === 'm' ? newKeyMinor[index] : newKey[index]);
66
66
  var transposedKey = newKeyName + keyName;
67
67
  var newKeySig = keyAccidentals(transposedKey);
68
- if (newKeySig.length > 0 && newKeySig[0].acc === 'flat')
68
+ if (newKeySig.length === 0 || newKeySig[0].acc === 'flat') // key of C and all keys with flats should have chords with flats
69
69
  multilineVars.localTransposePreferFlats = true;
70
70
  var distance = transposedKey.charCodeAt(0) - baseKey.charCodeAt(0);
71
71
  if (multilineVars.localTranspose > 0) {
@@ -45,6 +45,14 @@ function transposeChordName(chord, steps, preferFlats, freeGCchord) {
45
45
  else chord = sharpChords[index]
46
46
  }
47
47
 
48
+ var isDim = extra1 && (extra1.indexOf('dim') >= 0 || extra1.indexOf('°') >= 0)
49
+ //console.log(isDim, chord, extra1)
50
+ // We never want A#dim or D#dim
51
+ if (isDim && chord === 'A#') chord = 'Bb'
52
+ if (isDim && chord === 'D#') chord = 'Eb'
53
+ if (isDim && chord === 'A♯') chord = 'B♭'
54
+ if (isDim && chord === 'D♯') chord = 'E♭'
55
+
48
56
  if (extra1)
49
57
  chord += extra1
50
58
 
@@ -77,4 +85,4 @@ function transposeChordName(chord, steps, preferFlats, freeGCchord) {
77
85
  return chord;
78
86
  }
79
87
 
80
- module.exports = transposeChordName
88
+ module.exports = transposeChordName
@@ -9,7 +9,7 @@ var TuneBuilder = function (tune) {
9
9
  tune.reset();
10
10
 
11
11
  this.setVisualTranspose = function (visualTranspose) {
12
- if (visualTranspose)
12
+ if (visualTranspose!==undefined)
13
13
  tune.visualTranspose = visualTranspose;
14
14
  };
15
15
 
@@ -804,6 +804,8 @@ function wrapMusicLines(lines, barsperstaff) {
804
804
  }
805
805
 
806
806
  function getPrevMusicLine(lines, currentLine) {
807
+ if (lines.length <= currentLine)
808
+ return null
807
809
  // If the current line doesn't have music, search backwards until one is found.
808
810
  while (currentLine >= 0) {
809
811
  if (lines[currentLine].staff)
package/src/str/output.js CHANGED
@@ -58,12 +58,12 @@ var strTranspose;
58
58
  var count = arr[0].length
59
59
  for (var i = 1; i < arr.length; i++) {
60
60
  var segment = arr[i]
61
- var match = segment.match(/^( *)([A-G])([#b]?)(\w*)/)
61
+ var match = segment.match(/^( *)([A-G])([#b]?)( ?)(\w*)/)
62
62
  if (match) {
63
63
  var start = count + 2 + match[1].length // move past the 'K:' and optional white space
64
- var key = match[2] + match[3] + match[4] // key name, accidental, and mode
65
- var destinationKey = newKey({ root: match[2], acc: match[3], mode: match[4] }, steps)
66
- var dest = destinationKey.root + destinationKey.acc + destinationKey.mode
64
+ var key = match[2] + match[3] + match[4] + match[5] // key name, accidental, optional space, and mode
65
+ var destinationKey = newKey({ root: match[2], acc: match[3], mode: match[5] }, steps)
66
+ var dest = destinationKey.root + destinationKey.acc + match[4] + destinationKey.mode
67
67
  changes.push({ start: start, end: start + key.length, note: dest })
68
68
  }
69
69
  count += segment.length + 2
@@ -35,6 +35,7 @@ var pitchesToPerc = require('./pitches-to-perc');
35
35
  var stressBeat1 = 105;
36
36
  var stressBeatDown = 95;
37
37
  var stressBeatUp = 85;
38
+ var volumesPerNotePitch = [[stressBeat1, stressBeatDown, stressBeatUp]];
38
39
  var beatFraction = 0.25;
39
40
  var nextVolume;
40
41
  var nextVolumeDelta;
@@ -77,6 +78,7 @@ var pitchesToPerc = require('./pitches-to-perc');
77
78
  stressBeat1 = 105;
78
79
  stressBeatDown = 95;
79
80
  stressBeatUp = 85;
81
+ volumesPerNotePitch = [];
80
82
  beatFraction = 0.25;
81
83
  nextVolume = undefined;
82
84
  nextVolumeDelta = undefined;
@@ -193,6 +195,10 @@ var pitchesToPerc = require('./pitches-to-perc');
193
195
  stressBeat1 = element.beats[0];
194
196
  stressBeatDown = element.beats[1];
195
197
  stressBeatUp = element.beats[2];
198
+ if (!element.volumesPerNotePitch)
199
+ volumesPerNotePitch = []
200
+ else
201
+ volumesPerNotePitch = element.volumesPerNotePitch;
196
202
  // TODO-PER: also use the last parameter - which changes which beats are strong.
197
203
  break;
198
204
  case "vol":
@@ -343,28 +349,35 @@ var pitchesToPerc = require('./pitches-to-perc');
343
349
  return distanceFromStart / beatLength;
344
350
  }
345
351
 
346
- function processVolume(beat, voiceOff) {
352
+ function processVolume(beat, voiceOff, pitchIndexOfNote) {
347
353
  if (voiceOff)
348
354
  return 0;
349
-
355
+ let pitchStressBeat1 = stressBeat1;
356
+ let pitchStressBeatDown = stressBeatDown;
357
+ let pitchStressBeatUp = stressBeatUp;
358
+ if(pitchIndexOfNote !== undefined && volumesPerNotePitch.length >= pitchIndexOfNote+1){
359
+ pitchStressBeat1 = volumesPerNotePitch[pitchIndexOfNote][0];
360
+ pitchStressBeatDown = volumesPerNotePitch[pitchIndexOfNote][1];
361
+ pitchStressBeatUp = volumesPerNotePitch[pitchIndexOfNote][2];
362
+ }
350
363
  var volume;
351
364
  // MAE 21 Jun 2024 - This previously wasn't allowing zero volume to be applied
352
- if (nextVolume != undefined) {
365
+ if (nextVolume !== undefined) {
353
366
  volume = nextVolume;
354
367
  nextVolume = undefined;
355
368
  } else if (!doBeatAccents) {
356
- volume = stressBeatDown;
369
+ volume = pitchStressBeatDown;
357
370
  } else if (pickupLength > beat) {
358
- volume = stressBeatUp;
371
+ volume = pitchStressBeatUp;
359
372
  } else {
360
373
  //var barLength = meter.num / meter.den;
361
374
  var barBeat = calcBeat(lastBarTime, getBeatFraction(meter), beat);
362
375
  if (barBeat === 0)
363
- volume = stressBeat1;
376
+ volume = pitchStressBeat1;
364
377
  else if (parseInt(barBeat,10) === barBeat)
365
- volume = stressBeatDown;
378
+ volume = pitchStressBeatDown;
366
379
  else
367
- volume = stressBeatUp;
380
+ volume = pitchStressBeatUp;
368
381
  }
369
382
  if (nextVolumeDelta) {
370
383
  volume += nextVolumeDelta;
@@ -393,13 +406,17 @@ var pitchesToPerc = require('./pitches-to-perc');
393
406
  else if (elem.decoration[d] === 'lowermordent')
394
407
  ret.noteModification = "lowermordent";
395
408
  else if (elem.decoration[d] === 'uppermordent')
396
- ret.noteModification = "mordent";
409
+ ret.noteModification = "pralltriller";
397
410
  else if (elem.decoration[d] === 'mordent')
398
411
  ret.noteModification = "mordent";
399
412
  else if (elem.decoration[d] === 'turn')
400
413
  ret.noteModification = "turn";
401
414
  else if (elem.decoration[d] === 'roll')
402
415
  ret.noteModification = "roll";
416
+ else if (elem.decoration[d] === 'pralltriller')
417
+ ret.noteModification = "pralltriller";
418
+ else if (elem.decoration[d] === 'trillh')
419
+ ret.noteModification = "trillh";
403
420
  }
404
421
  }
405
422
  return ret;
@@ -423,7 +440,25 @@ var pitchesToPerc = require('./pitches-to-perc');
423
440
  start += shortestNote;
424
441
  }
425
442
  break;
426
- case "mordent":
443
+ case "trillh":
444
+ var note = 1;
445
+ while (runningDuration > 0) {
446
+ currentTrack.push({
447
+ cmd: 'note',
448
+ pitch: p.pitch + note,
449
+ volume: p.volume,
450
+ start: start,
451
+ duration: shortestNote,
452
+ gap: 0,
453
+ instrument: currentInstrument,
454
+ style: 'decoration'
455
+ });
456
+ note = note === 1 ? 0 : 1;
457
+ runningDuration -= shortestNote;
458
+ start += shortestNote;
459
+ }
460
+ break;
461
+ case "pralltriller":
427
462
  currentTrack.push({ cmd: 'note', pitch: p.pitch, volume: p.volume, start: start, duration: shortestNote, gap: 0, instrument: currentInstrument, style: 'decoration' });
428
463
  runningDuration -= shortestNote;
429
464
  start += shortestNote;
@@ -432,6 +467,7 @@ var pitchesToPerc = require('./pitches-to-perc');
432
467
  start += shortestNote;
433
468
  currentTrack.push({ cmd: 'note', pitch: p.pitch, volume: p.volume, start: start, duration: runningDuration, gap: 0, instrument: currentInstrument });
434
469
  break;
470
+ case "mordent":
435
471
  case "lowermordent":
436
472
  currentTrack.push({ cmd: 'note', pitch: p.pitch, volume: p.volume, start: start, duration: shortestNote, gap: 0, instrument: currentInstrument, style: 'decoration' });
437
473
  runningDuration -= shortestNote;
@@ -532,6 +568,11 @@ var pitchesToPerc = require('./pitches-to-perc');
532
568
  if (elem.elem)
533
569
  elem.elem.midiPitches = [];
534
570
  for (var i=0; i<ePitches.length; i++) {
571
+ //here we can set the volume for each note in a chord, if specified
572
+ let pitchVelocity = velocity;
573
+ if(!ret.velocity && Array.isArray(elem.decoration) && elem.decoration.length > i){
574
+ pitchVelocity = processVolume(timeToRealTime(elem.time), voiceOff, i)
575
+ }
535
576
  var note = ePitches[i];
536
577
  if (!note)
537
578
  continue;
@@ -545,7 +586,7 @@ var pitchesToPerc = require('./pitches-to-perc');
545
586
  if (name && percmap[name])
546
587
  actualPitch = percmap[name].sound;
547
588
  }
548
- var p = { cmd: 'note', pitch: actualPitch, volume: velocity, start: timeToRealTime(elem.time), duration: durationRounded(note.duration), instrument: currentInstrument, startChar: elem.elem.startChar, endChar: elem.elem.endChar};
589
+ var p = { cmd: 'note', pitch: actualPitch, volume: pitchVelocity, start: timeToRealTime(elem.time), duration: durationRounded(note.duration), instrument: currentInstrument, startChar: elem.elem.startChar, endChar: elem.elem.endChar};
549
590
  p = adjustForMicroTone(p);
550
591
  if (elem.gracenotes) {
551
592
  p.duration = p.duration / 2;
@@ -135,6 +135,7 @@ var parseCommon = require("../parse/abc_common");
135
135
 
136
136
  // visit each voice completely in turn
137
137
  var voices = [];
138
+ var clefTransposeActive = []
138
139
  var inCrescendo = [];
139
140
  var inDiminuendo = [];
140
141
  var durationCounter = [0];
@@ -188,12 +189,24 @@ var parseCommon = require("../parse/abc_common");
188
189
  if (staff.clef && staff.clef.type !== "perc" && staff.clef.transpose) {
189
190
  staff.clef.el_type = 'clef';
190
191
  voices[voiceNumber].push({ el_type: 'transpose', transpose: staff.clef.transpose });
192
+ clefTransposeActive[voiceNumber] = false
191
193
  }
192
194
  if (staff.clef && staff.clef.type) {
193
- if (staff.clef.type.indexOf("-8") >= 0)
194
- voices[voiceNumber].push({ el_type: 'transpose', transpose: -12 });
195
- else if (staff.clef.type.indexOf("+8") >= 0)
196
- voices[voiceNumber].push({ el_type: 'transpose', transpose: 12 });
195
+ if (staff.clef.type.indexOf("-8") >= 0) {
196
+ voices[voiceNumber].push({el_type: 'transpose', transpose: -12});
197
+ clefTransposeActive[voiceNumber] = true
198
+ }
199
+ else if (staff.clef.type.indexOf("+8") >= 0) {
200
+ voices[voiceNumber].push({el_type: 'transpose', transpose: 12});
201
+ clefTransposeActive[voiceNumber] = true
202
+ }
203
+ else {
204
+ // if we had a previous treble+8 and now have a regular clef, then cancel the transposition
205
+ if (clefTransposeActive[voiceNumber]) {
206
+ voices[voiceNumber].push({ el_type: 'transpose', transpose: 0 });
207
+ clefTransposeActive[voiceNumber] = false
208
+ }
209
+ }
197
210
  }
198
211
 
199
212
  if (abctune.formatting.midi && abctune.formatting.midi.drumoff) {
@@ -429,7 +442,7 @@ var parseCommon = require("../parse/abc_common");
429
442
  }
430
443
 
431
444
  function setDynamics(elem) {
432
- var volumes = {
445
+ var volumes = {//stressBeat1, stressBeatDown, stressBeatUp
433
446
  'pppp': [15, 10, 5, 1],
434
447
  'ppp': [30, 20, 10, 1],
435
448
  'pp': [45, 35, 20, 1],
@@ -467,7 +480,15 @@ var parseCommon = require("../parse/abc_common");
467
480
 
468
481
  if (dynamicType) {
469
482
  currentVolume = volumes[dynamicType].slice(0);
470
- voices[voiceNumber].push({ el_type: 'beat', beats: currentVolume.slice(0) });
483
+ let volumesPerNotePitch = [currentVolume];
484
+ if(Array.isArray(elem.decoration)){
485
+ volumesPerNotePitch = [];
486
+ elem.decoration.forEach(d=>{
487
+ if (d in volumes)
488
+ volumesPerNotePitch.push(volumes[d].slice(0));
489
+ });
490
+ }
491
+ voices[voiceNumber].push({ el_type: 'beat', beats: currentVolume.slice(0), volumesPerNotePitch: volumesPerNotePitch, });
471
492
  inCrescendo[k] = false;
472
493
  inDiminuendo[k] = false;
473
494
  }
@@ -183,7 +183,7 @@ ChordTrack.prototype.interpretChord = function (name) {
183
183
  return { chick: [] };
184
184
  var root = name.substring(0, 1);
185
185
  if (root === '(') {
186
- name = name.substring(1, name.length - 2);
186
+ name = name.substring(1, name.length - 1);
187
187
  if (name.length === 0)
188
188
  return undefined;
189
189
  root = name.substring(0, 1);
@@ -26,7 +26,7 @@ function CreateSynth() {
26
26
  self.audioBuffers = []; // cache of the buffers so starting play can be fast.
27
27
  self.duration = undefined; // the duration of the tune in seconds.
28
28
  self.isRunning = false; // whether there is currently a sound buffer running.
29
- self.options = undefined
29
+ self.options = {} // Thx tomohirohiratsuka
30
30
  self.pickupLength = 0
31
31
 
32
32
  // Load and cache all needed sounds
@@ -299,82 +299,91 @@ function CreateSynth() {
299
299
  return Promise.reject(new Error(notSupportedMessage));
300
300
  if (self.debugCallback)
301
301
  self.debugCallback("prime called");
302
- return new Promise(function(resolve) {
303
- var startTime = activeAudioContext().currentTime;
304
- var tempoMultiplier = self.millisecondsPerMeasure / 1000 / self.meterSize;
305
- self.duration = self.flattened.totalDuration * tempoMultiplier;
306
- if(self.duration <= 0) {
307
- self.audioBuffers = [];
308
- return resolve({ status: "empty", seconds: 0});
309
- }
310
- self.duration += fadeTimeSec;
311
- var totalSamples = Math.floor(activeAudioContext().sampleRate * self.duration);
312
302
 
313
- // There might be a previous run that needs to be turned off.
314
- self.stop();
303
+ return new Promise(function(resolve, reject) {
304
+ try {
305
+ var startTime = activeAudioContext().currentTime;
306
+ var tempoMultiplier = self.millisecondsPerMeasure / 1000 / self.meterSize;
307
+ self.duration = self.flattened.totalDuration * tempoMultiplier;
308
+ if (self.duration <= 0) {
309
+ self.audioBuffers = [];
310
+ return resolve({status: "empty", seconds: 0});
311
+ }
312
+ self.duration += fadeTimeSec;
313
+ var totalSamples = Math.floor(activeAudioContext().sampleRate * self.duration);
315
314
 
316
- var noteMapTracks = createNoteMap(self.flattened);
315
+ // There might be a previous run that needs to be turned off.
316
+ self.stop();
317
317
 
318
- if (self.options.swing)
319
- addSwing(noteMapTracks, self.options.swing, self.meterFraction, self.pickupLength)
318
+ var noteMapTracks = createNoteMap(self.flattened);
320
319
 
321
- if (self.sequenceCallback)
322
- self.sequenceCallback(noteMapTracks, self.callbackContext);
320
+ if (self.options.swing)
321
+ addSwing(noteMapTracks, self.options.swing, self.meterFraction, self.pickupLength)
323
322
 
324
- var panDistances = setPan(noteMapTracks.length, self.pan);
323
+ if (self.sequenceCallback)
324
+ self.sequenceCallback(noteMapTracks, self.callbackContext);
325
325
 
326
- // Create a simple list of all the unique sounds in this music and where they should be placed.
327
- // There appears to be a limit on how many audio buffers can be created at once so this technique limits the number needed.
328
- var uniqueSounds = {};
329
- noteMapTracks.forEach(function(noteMap, trackNumber) {
330
- var panDistance = panDistances && panDistances.length > trackNumber ? panDistances[trackNumber] : 0;
331
- noteMap.forEach(function(note) {
332
- var key = note.instrument + ':' + note.pitch + ':' +note.volume + ':' + Math.round((note.end-note.start)*1000)/1000 + ':' + panDistance + ':' + tempoMultiplier + ':' + (note.cents ? note.cents : 0);
333
- if (self.debugCallback)
334
- self.debugCallback("noteMapTrack "+key)
335
- if (!uniqueSounds[key])
336
- uniqueSounds[key] = [];
337
- uniqueSounds[key].push(note.start);
326
+ var panDistances = setPan(noteMapTracks.length, self.pan);
327
+
328
+ // Create a simple list of all the unique sounds in this music and where they should be placed.
329
+ // There appears to be a limit on how many audio buffers can be created at once so this technique limits the number needed.
330
+ var uniqueSounds = {};
331
+ noteMapTracks.forEach(function (noteMap, trackNumber) {
332
+ var panDistance = panDistances && panDistances.length > trackNumber ? panDistances[trackNumber] : 0;
333
+ noteMap.forEach(function (note) {
334
+ var key = note.instrument + ':' + note.pitch + ':' + note.volume + ':' + Math.round((note.end - note.start) * 1000) / 1000 + ':' + panDistance + ':' + tempoMultiplier + ':' + (note.cents ? note.cents : 0);
335
+ if (self.debugCallback)
336
+ self.debugCallback("noteMapTrack " + key)
337
+ if (!uniqueSounds[key])
338
+ uniqueSounds[key] = [];
339
+ uniqueSounds[key].push(note.start);
340
+ });
338
341
  });
339
- });
340
342
 
341
- // Now that we know what we are trying to create, construct the audio buffer by creating each sound and placing it.
342
- var allPromises = [];
343
- var audioBuffer = activeAudioContext().createBuffer(2, totalSamples, activeAudioContext().sampleRate);
344
- for (var key2 = 0; key2 < Object.keys(uniqueSounds).length; key2++) {
345
- var k = Object.keys(uniqueSounds)[key2];
346
- var parts = k.split(":");
347
- var cents = parts[6] !== undefined ? parseFloat(parts[6]) : 0;
348
- parts = {instrument: parts[0], pitch: parseInt(parts[1], 10), volume: parseInt(parts[2], 10), len: parseFloat(parts[3]), pan: parseFloat(parts[4]), tempoMultiplier: parseFloat(parts[5]), cents: cents};
349
- allPromises.push(placeNote(audioBuffer, activeAudioContext().sampleRate, parts, uniqueSounds[k], self.soundFontVolumeMultiplier, self.programOffsets[parts.instrument], fadeTimeSec, self.noteEnd/1000, self.debugCallback));
350
- }
351
- self.audioBuffers = [audioBuffer];
343
+ // Now that we know what we are trying to create, construct the audio buffer by creating each sound and placing it.
344
+ var allPromises = [];
345
+ var audioBuffer = activeAudioContext().createBuffer(2, totalSamples, activeAudioContext().sampleRate);
346
+ for (var key2 = 0; key2 < Object.keys(uniqueSounds).length; key2++) {
347
+ var k = Object.keys(uniqueSounds)[key2];
348
+ var parts = k.split(":");
349
+ var cents = parts[6] !== undefined ? parseFloat(parts[6]) : 0;
350
+ parts = {instrument: parts[0], pitch: parseInt(parts[1], 10), volume: parseInt(parts[2], 10), len: parseFloat(parts[3]), pan: parseFloat(parts[4]), tempoMultiplier: parseFloat(parts[5]), cents: cents};
351
+ allPromises.push(placeNote(audioBuffer, activeAudioContext().sampleRate, parts, uniqueSounds[k], self.soundFontVolumeMultiplier, self.programOffsets[parts.instrument], fadeTimeSec, self.noteEnd / 1000, self.debugCallback));
352
+ }
353
+ self.audioBuffers = [audioBuffer];
352
354
 
353
- if (self.debugCallback) {
354
- self.debugCallback("sampleRate = " + activeAudioContext().sampleRate);
355
- self.debugCallback("totalSamples = " + totalSamples);
356
- self.debugCallback("creationTime = " + Math.floor((activeAudioContext().currentTime - startTime)*1000) + "ms");
357
- }
358
- function resolveData(me) {
359
- var duration = me && me.audioBuffers && me.audioBuffers.length > 0 ? me.audioBuffers[0].duration : 0;
360
- return { status: activeAudioContext().state, duration: duration}
361
- }
362
- Promise.all(allPromises).then(function() {
363
- // Safari iOS can mess with the audioContext state, so resume if needed.
364
- if (activeAudioContext().state === "suspended") {
365
- activeAudioContext().resume().then(function () {
366
- resolve(resolveData(self));
367
- })
368
- } else if (activeAudioContext().state === "interrupted") {
369
- activeAudioContext().suspend().then(function () {
355
+ if (self.debugCallback) {
356
+ self.debugCallback("sampleRate = " + activeAudioContext().sampleRate);
357
+ self.debugCallback("totalSamples = " + totalSamples);
358
+ self.debugCallback("creationTime = " + Math.floor((activeAudioContext().currentTime - startTime) * 1000) + "ms");
359
+ }
360
+
361
+ function resolveData(me) {
362
+ var duration = me && me.audioBuffers && me.audioBuffers.length > 0 ? me.audioBuffers[0].duration : 0;
363
+ return {status: activeAudioContext().state, duration: duration}
364
+ }
365
+
366
+ Promise.all(allPromises).then(function () {
367
+ // Safari iOS can mess with the audioContext state, so resume if needed.
368
+ if (activeAudioContext().state === "suspended") {
370
369
  activeAudioContext().resume().then(function () {
371
370
  resolve(resolveData(self));
372
371
  })
373
- })
374
- } else {
375
- resolve(resolveData(self));
376
- }
377
- });
372
+ } else if (activeAudioContext().state === "interrupted") {
373
+ activeAudioContext().suspend().then(function () {
374
+ activeAudioContext().resume().then(function () {
375
+ resolve(resolveData(self));
376
+ })
377
+ })
378
+ } else {
379
+ resolve(resolveData(self));
380
+ }
381
+ }).catch(function (error) {
382
+ reject(error)
383
+ });
384
+ } catch (error) {
385
+ reject(error)
386
+ }
378
387
  });
379
388
  };
380
389