smoosic 1.0.36 → 1.0.38

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 (42) hide show
  1. package/README.md +6 -3
  2. package/build/smoosic.js +89 -29
  3. package/changes.md +9 -1
  4. package/package.json +1 -1
  5. package/release/html/libmode.html +36 -0
  6. package/release/html/smoosic.html +4 -4
  7. package/release/smoosic.js +89 -29
  8. package/release/styles/dialogs.css +25 -18
  9. package/release/styles/general.css +16 -9
  10. package/release/styles/media.css +13 -5
  11. package/release/styles/ribbon.css +3 -0
  12. package/src/application/exports.ts +4 -4
  13. package/src/render/audio/musicCursor.ts +1 -1
  14. package/src/render/sui/NoteEntryCaret.ts +4 -1
  15. package/src/render/sui/formatter.ts +3 -3
  16. package/src/render/sui/mapper.ts +1 -1
  17. package/src/render/sui/scoreView.ts +4 -4
  18. package/src/render/sui/scoreViewOperations.ts +10 -10
  19. package/src/render/sui/textEdit.ts +3 -1
  20. package/src/render/vex/vxMeasure.ts +12 -6
  21. package/src/smo/data/measure.ts +37 -37
  22. package/src/smo/data/measureModifiers.ts +106 -71
  23. package/src/smo/data/note.ts +4 -1
  24. package/src/smo/data/score.ts +50 -11
  25. package/src/smo/data/systemStaff.ts +2 -2
  26. package/src/smo/midi/midiToSmo.ts +28 -29
  27. package/src/smo/midi/smoToMidi.ts +3 -3
  28. package/src/smo/mxml/smoToXml.ts +11 -11
  29. package/src/smo/mxml/xmlState.ts +3 -3
  30. package/src/smo/mxml/xmlToSmo.ts +9 -9
  31. package/src/smo/xform/copypaste.ts +3 -3
  32. package/src/smo/xform/operations.ts +9 -6
  33. package/src/smo/xform/tickDuration.ts +10 -2
  34. package/src/ui/buttons/display.ts +2 -2
  35. package/src/ui/buttons/ribbon.ts +2 -2
  36. package/src/ui/components/dialogs/timeSignature.vue +223 -0
  37. package/src/ui/dialogs/fileDialogs.ts +1 -1
  38. package/src/ui/dialogs/keySignature.ts +1 -1
  39. package/src/ui/dialogs/tempo.ts +38 -38
  40. package/src/ui/dialogs/timeSignature.ts +45 -116
  41. package/src/ui/menus/timeSignature.ts +2 -2
  42. package/tools/smoosic-schema.json +4 -4
@@ -250,7 +250,7 @@ body .vueDialogContainer>div {
250
250
  body.showVueDialog .vueDialogContainer,
251
251
  body.showVueDialog .modal{
252
252
  position:absolute;
253
- display:block;
253
+ /* display:block; */
254
254
  z-index:3;
255
255
  }
256
256
  .draggable-button {
@@ -495,14 +495,29 @@ button.nav-link {
495
495
  margin:10px;
496
496
  }
497
497
 
498
+ .helpDialog .button-left,
498
499
  .attributeModal .buttonContainer .button-left {
499
500
  margin-right:5px;
500
501
  }
502
+ .helpDialog .button-left {
503
+ padding-left: 10px;
504
+ }
505
+ .helpDialog .button-right {
506
+ padding-right: 10px;
507
+ }
508
+ .prev-topic-text {
509
+ margin-left: 5px;
510
+ }
511
+ .next-topic-text {
512
+ margin-right: 5px;
513
+ }
514
+ .helpDialog .button-center,
501
515
  .attributeModal .buttonContainer .button-center {
502
516
  margin-right:5px;
503
517
  margin-left:5px;
504
518
  }
505
519
 
520
+ .helpDialog .button-right,
506
521
  .attributeModal .buttonContainer .button-right {
507
522
  margin-left:5px;
508
523
  }
@@ -900,7 +915,7 @@ body.bugReport .bug-modal {
900
915
  display: flex;
901
916
  }
902
917
 
903
- .bug-modal .modal {
918
+ .modal.show {
904
919
  display: block;
905
920
  }
906
921
  .bug-title {
@@ -923,17 +938,15 @@ body.splashScreen .bugDialog {
923
938
 
924
939
  }
925
940
 
926
- .helpDialog {
927
- display:none;
928
- }
941
+
929
942
  /* change did not get propogated */
930
- body.showHelpDialog .helpDialog {
943
+ #help-dialog-container .helpDialog {
931
944
  display:flex;
932
945
  overflow: hidden;
933
946
  background: #eef;
934
947
  flex: 1 0 100%;
935
948
  flex-flow:column wrap;
936
- overflow:auto;
949
+ overflow-y:auto;
937
950
  margin: 8px;
938
951
  padding: 10px;
939
952
  position: absolute;
@@ -984,7 +997,7 @@ body.showHelpDialog .helpDialog {
984
997
  position: absolute;
985
998
  left: 2px;
986
999
  }
987
- body.showHelpDialog .helpDialog button.close {
1000
+ .helpDialog button.close {
988
1001
  position: absolute;
989
1002
  right: 5px;
990
1003
  color: red;
@@ -1161,7 +1174,7 @@ button.translate-submit-button {
1161
1174
  border-radius: 4px;
1162
1175
  }
1163
1176
 
1164
- #renderProgress {
1177
+ #render-progress {
1165
1178
  display: none;
1166
1179
  width:200px;
1167
1180
  height:25px;
@@ -1175,20 +1188,14 @@ button.translate-submit-button {
1175
1188
  font-family: Arial, Helvetica, sans-serif;
1176
1189
  font-size: 0.8em;
1177
1190
  }
1178
- #renderProgress:after {
1179
- margin: -25px 0 0 100px;
1180
- padding:0;
1181
- display:inline-block;
1182
- content: attr(value) '%';
1183
- }
1184
- #renderProgress[value]::-webkit-progress-bar {
1191
+ #render-progress[value]::-webkit-progress-bar {
1185
1192
  background-color:#eee;
1186
1193
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25) inset;
1187
1194
  }
1188
1195
 
1189
- #renderProgress[value]::-webkit-progress-value {
1196
+ #render-progress[value]::-webkit-progress-value {
1190
1197
  background: #F70;
1191
1198
  }
1192
- body.show-render-progress #renderProgress {
1199
+ body.progress-modal #render-progress {
1193
1200
  display: block;
1194
1201
  }
@@ -1,15 +1,22 @@
1
- .pitch-preview {
2
- cursor: default;
3
- pointer-events: none;
1
+ .musicContainer {
4
2
  user-select: none;
5
3
  -webkit-user-select: none;
6
4
  -moz-user-select: none;
7
5
  -ms-user-select: none;
8
6
  }
9
7
 
10
- .note-entry-caret {
11
- user-select: none;
12
- -webkit-user-select: none;
13
- -moz-user-select: none;
14
- -ms-user-select: none;
15
- }
8
+ .pitch-preview {
9
+ cursor: default;
10
+ pointer-events: none;
11
+ /*user-select: none;*/
12
+ /*-webkit-user-select: none;*/
13
+ /*-moz-user-select: none;*/
14
+ /*-ms-user-select: none;*/
15
+ }
16
+
17
+ /*.note-entry-caret {*/
18
+ /* user-select: none;*/
19
+ /* -webkit-user-select: none;*/
20
+ /* -moz-user-select: none;*/
21
+ /* -ms-user-select: none;*/
22
+ /*}*/
@@ -131,7 +131,7 @@ body {
131
131
  }
132
132
  /* override bootstrap */
133
133
  body.modal {
134
- display: block;
134
+ display: block;
135
135
  }
136
136
  li.nav-item button.nav-link {
137
137
  text-align: left;
@@ -167,7 +167,7 @@ h1 {
167
167
  .testTitle {
168
168
  height: 20px;
169
169
  font-size:1.2em;
170
- margin:0;
170
+ margin:0;
171
171
  }
172
172
 
173
173
  #birdy {
@@ -319,6 +319,15 @@ body.printing .printFrame {
319
319
  flex-flow:column nowrap;
320
320
  flex: 1 1 auto;
321
321
  }
322
+ .musicRelief .score-container div {
323
+ background-color: #fff;
324
+ }
325
+ .horizontal.musicRelief {
326
+ flex-flow:row nowrap;
327
+ }
328
+ .horizontal.musicRelief .score-container {
329
+ display: flex;
330
+ }
322
331
  .workspace {
323
332
  padding-top: 2px;
324
333
  position:relative;
@@ -330,7 +339,6 @@ body.printing .printFrame {
330
339
 
331
340
  .musicRelief {
332
341
  background:#99a;
333
-
334
342
  }
335
343
  .media {
336
344
  display:flex;
@@ -344,8 +352,8 @@ body.printing .printFrame {
344
352
  display:none;
345
353
  }
346
354
 
347
- .musicContainer {
348
- position: relative;
355
+ .score-container {
356
+ position: relative;
349
357
  background:#fff;
350
358
  }
351
359
  .overlay {
@@ -192,6 +192,9 @@ body.alt-key button.hover-text .ribbon-button-hotkey {
192
192
  .hide {
193
193
  display: none !important;
194
194
  }
195
+ .invisible {
196
+ visibility: hidden !important;
197
+ }
195
198
  .controls-top button.duration.expanded.tuplet span.ribbon-button-hotkey {
196
199
  font-size:1em;
197
200
  padding-left: 7px;
@@ -37,7 +37,7 @@ import { SuiTransposeScoreDialogVue} from '../ui/dialogs/transposeScore';
37
37
  import { SuiMeasureFormatDialogVue } from '../ui/dialogs/measureFormat';
38
38
  import { SuiInsertMeasuresVue } from '../ui/dialogs/addMeasure';
39
39
  import { SuiInstrumentDialogVue } from '../ui/dialogs/instrument';
40
- import { SuiTimeSignatureDialog } from '../ui/dialogs/timeSignature';
40
+ import { SuiTimeSignatureDialogVue } from '../ui/dialogs/timeSignature';
41
41
  import { SuiTempoDialog } from '../ui/dialogs/tempo';
42
42
  import { SuiNoteHeadDialog } from '../ui/dialogs/noteHead';
43
43
  import { SuiEndingsDialogVue } from '../ui/dialogs/endings';
@@ -153,7 +153,7 @@ import { SmoOperation } from '../smo/xform/operations';
153
153
  import { ScoreRoadMapBuilder } from '../smo/xform/roadmap';
154
154
  import {
155
155
  SmoRehearsalMark, SmoMeasureFormat, SmoBarline, SmoRepeatSymbol,
156
- SmoVolta, SmoMeasureText, SmoTempoText, TimeSignature, measureModifierDynamicCtorInit
156
+ SmoVolta, SmoMeasureText, SmoTempo, SmoTimeSignature, measureModifierDynamicCtorInit
157
157
  } from '../smo/data/measureModifiers';
158
158
  import { SmoToXml } from '../smo/mxml/smoToXml';
159
159
  import { MidiToSmo } from '../smo/midi/midiToSmo';
@@ -357,7 +357,7 @@ export const Smo = {
357
357
  GlobalLayoutNumberAttributesArray,
358
358
  SuiTransposeScoreDialogVue,
359
359
  SuiScoreFontDialogVue, SuiPageLayoutDialogVue, SuiMeasureFormatDialogVue, SuiInsertMeasuresVue,
360
- SuiTimeSignatureDialog,SuiTextBlockDialog, SuiLyricDialog, SuiChordChangeDialog,
360
+ SuiTimeSignatureDialogVue,SuiTextBlockDialog, SuiLyricDialog, SuiChordChangeDialog,
361
361
  SuiSlurAttributesDialog, SuiPedalMarkingDialog, SuiTieAttributesDialog, SuiVoltaAttributeDialog,
362
362
  SuiHairpinAttributesDialog, SuiStaffGroupDialogVue,
363
363
  SuiScorePreferencesDialogVue,
@@ -400,7 +400,7 @@ SuiPitchComposite,
400
400
  // score modifiers
401
401
  SmoSystemGroup, SmoAudioPlayerSettings, SmoTextGroup,
402
402
  // measure modifiers
403
- SmoRehearsalMark, SmoMeasureFormat, SmoBarline, SmoRepeatSymbol, SmoVolta, SmoMeasureText, SmoTempoText, TimeSignature,
403
+ SmoRehearsalMark, SmoMeasureFormat, SmoBarline, SmoRepeatSymbol, SmoVolta, SmoMeasureText, SmoTempo, SmoTimeSignature,
404
404
  measureModifierDynamicCtorInit,
405
405
  // note modifiers
406
406
  SmoOrnament, noteModifierDynamicCtorInit,
@@ -72,7 +72,7 @@ export const defaultClearAudioAnimationHandler = (delay: number) => {
72
72
  }
73
73
  const screenBox = SvgHelpers.boxPoints(x, y, width, height);
74
74
  const fillParams: Record<string, string> = {};
75
- console.log(`topbox xy ${measure.svg.logicalBox.x}, ${measure.svg.logicalBox.y} x ${x}, ${y}`);
75
+ // console.log(`topbox xy ${measure.svg.logicalBox.x}, ${measure.svg.logicalBox.y} x ${x}, ${y}`);
76
76
  fillParams['fill-opacity'] = '0.5';
77
77
  fillParams['fill'] = '#4444ff';
78
78
  const ctx = context.getContext();
@@ -203,7 +203,10 @@ export class NoteEntryCaret {
203
203
  }
204
204
 
205
205
  const staveNote = vexNote as StaveNote;
206
- const noteHeads = staveNote.noteHeads;
206
+ if (!staveNote.noteHeads) {
207
+ return;
208
+ }
209
+ const noteHeads = staveNote.noteHeads;
207
210
 
208
211
  this.vexNoteLeftDisplacedHeadPx = staveNote.getMetrics().leftDisplacedHeadPx;
209
212
  this.vexNoteRightDisplacedHeadPx = staveNote.getMetrics().rightDisplacedHeadPx;
@@ -16,7 +16,7 @@ import { SmoStaffHairpin, SmoStaffTextBracket, SmoTabStave } from '../../smo/dat
16
16
  import { layoutDebug } from './layoutDebug';
17
17
  import { ScaledPageLayout, SmoLayoutManager, SmoPageLayout } from '../../smo/data/scoreModifiers';
18
18
  import { SmoMeasure, ISmoBeamGroup } from '../../smo/data/measure';
19
- import { TimeSignature, SmoTempoText } from '../../smo/data//measureModifiers';
19
+ import { SmoTimeSignature, SmoTempo } from '../../smo/data//measureModifiers';
20
20
  import { SvgPageMap } from './svgPageMap';
21
21
  import { VexFlow, defaultMeasurePadding } from '../../common/vex';
22
22
  import { TextFormatter } from '../../common/textformatter';
@@ -831,7 +831,7 @@ export class SuiLayoutFormatter {
831
831
  * @param score
832
832
  */
833
833
  calculateBeginningSymbols(systemIndex: number, measure: SmoMeasure,
834
- clefLast: string, keySigLast: string, timeSigLast: TimeSignature, tempoLast: SmoTempoText) {
834
+ clefLast: string, keySigLast: string, timeSigLast: SmoTimeSignature, tempoLast: SmoTempo) {
835
835
  // The key signature is set based on the transpose index already, i.e. an Eb part in concert C already has 3 sharps.
836
836
  const xposeScore = this.score?.preferences?.transposingScore && (this.score?.isPartExposed() === false);
837
837
  // const xposeOffset = xposeScore ? measure.transposeIndex : 0;
@@ -850,7 +850,7 @@ export class SuiLayoutFormatter {
850
850
  measure.svg.forceTempo = tempo.display && measure.svg.rowInSystem === 0;
851
851
  } else if (tempo && tempoLast) {
852
852
  // otherwise get tempo from the measure prior. But only one tempo per system.
853
- if (!SmoTempoText.eq(tempo, tempoLast) && measure.svg.rowInSystem === 0) {
853
+ if (!SmoTempo.eq(tempo, tempoLast) && measure.svg.rowInSystem === 0) {
854
854
  measure.svg.forceTempo = tempo.display;
855
855
  }
856
856
  } else if (tempo) {
@@ -159,7 +159,7 @@ export abstract class SuiMapper {
159
159
  this.localModifiers.push({ index, selection: sel, modifier: volta, box: volta.logicalBox ?? SvgBox.default });
160
160
  index += 1;
161
161
  });
162
- sel.measure.getModifiersByType('SmoTempoText').forEach((tempo) => {
162
+ sel.measure.getModifiersByType('SmoTempo').forEach((tempo) => {
163
163
  this.localModifiers.push({ index, selection: sel, modifier: tempo, box: tempo.logicalBox ?? SvgBox.default });
164
164
  index += 1;
165
165
  });
@@ -23,8 +23,8 @@ import { ScoreRenderParams } from './scoreRender';
23
23
  import { SmoOperation } from '../../smo/xform/operations';
24
24
  import { SuiAudioPlayer } from '../audio/player';
25
25
  import { SuiAudioAnimationParams } from '../audio/musicCursor';
26
- import { SmoTempoText } from '../../smo/data/measureModifiers';
27
- import { TimeSignature } from '../../smo/data/measureModifiers';
26
+ import { SmoTempo } from '../../smo/data/measureModifiers';
27
+ import { SmoTimeSignature } from '../../smo/data/measureModifiers';
28
28
  import { ref, Ref } from 'vue';
29
29
  import { SvgBox, SvgPoint } from '../../smo/data/common';
30
30
  import { layoutDebug } from './layoutDebug';
@@ -685,8 +685,8 @@ export abstract class SuiScoreView implements layoutProvider {
685
685
  }
686
686
  nStave.measures.forEach((measure: SmoMeasure, ix) => {
687
687
  const srcMeasure = srcStave.measures[ix];
688
- measure.tempo = new SmoTempoText(srcMeasure.tempo.serialize());
689
- measure.timeSignature = new TimeSignature(srcMeasure.timeSignature);
688
+ measure.tempo = new SmoTempo(srcMeasure.tempo.serialize());
689
+ measure.timeSignature = new SmoTimeSignature(srcMeasure.timeSignature);
690
690
  measure.keySignature = srcMeasure.keySignature;
691
691
  });
692
692
  staffMap.push(i);
@@ -14,8 +14,8 @@ import { SmoTextGroup } from '../../smo/data/scoreText';
14
14
  import { SmoDynamicText, SmoNoteModifierBase, SmoGraceNote, SmoArticulation,
15
15
  SmoOrnament, SmoLyric, SmoMicrotone, SmoArpeggio, SmoArpeggioType, SmoClefChange,
16
16
  SmoTabNote} from '../../smo/data/noteModifiers';
17
- import { SmoTempoText, SmoVolta, SmoBarline, SmoRepeatSymbol,
18
- SmoRehearsalMark, SmoMeasureFormat, TimeSignature } from '../../smo/data/measureModifiers';
17
+ import { SmoTempo, SmoVolta, SmoBarline, SmoRepeatSymbol,
18
+ SmoRehearsalMark, SmoMeasureFormat, SmoTimeSignature } from '../../smo/data/measureModifiers';
19
19
  import { UndoBuffer } from '../../smo/xform/undo';
20
20
  import {
21
21
  SmoOperation, createStaffModifierType, MakeTupletOperation
@@ -525,7 +525,7 @@ export class SuiScoreViewOperations extends SuiScoreView {
525
525
  * Set the time signature for a selection
526
526
  * @param timeSignature actual time signature
527
527
  */
528
- async setTimeSignature(timeSignature: TimeSignature): Promise<void> {
528
+ async setTimeSignature(timeSignature: SmoTimeSignature): Promise<void> {
529
529
  this._undoScore('Set time signature');
530
530
  const selections = this.tracker.selections;
531
531
  const altSelections = this._getEquivalentSelections(selections);
@@ -627,9 +627,9 @@ export class SuiScoreViewOperations extends SuiScoreView {
627
627
  * @param scoreMode if true, update whole score. Else selections
628
628
  * @returns
629
629
  */
630
- async updateTempoScore(measure: SmoMeasure, tempo: SmoTempoText, scoreMode: boolean, selectionMode: boolean): Promise<void> {
630
+ async updateTempoScore(measure: SmoMeasure, tempo: SmoTempo, scoreMode: boolean, selectionMode: boolean): Promise<void> {
631
631
  let measureIndex = 0;
632
- const originalTempo = new SmoTempoText(measure.tempo);
632
+ const originalTempo = new SmoTempo(measure.tempo);
633
633
  this._undoColumn('update tempo', measure.measureNumber.measureIndex);
634
634
  let startMeasure = measure.measureNumber.measureIndex;
635
635
  let endMeasure = this.score.staves[0].measures.length;
@@ -641,14 +641,14 @@ export class SuiScoreViewOperations extends SuiScoreView {
641
641
  }
642
642
  }
643
643
  // If we are only changing the position of the text, it only affects the tempo measure.
644
- if (SmoTempoText.eq(originalTempo, tempo) && tempo.yOffset !== originalTempo.yOffset && endMeasure > startMeasure) {
644
+ if (SmoTempo.eq(originalTempo, tempo) && tempo.yOffset !== originalTempo.yOffset && endMeasure > startMeasure) {
645
645
  endMeasure = startMeasure + 1;
646
646
  }
647
647
  for (measureIndex = startMeasure; measureIndex < endMeasure; ++measureIndex) {
648
648
  if (!scoreMode && !selectionMode) {
649
649
  // If not whole score or selections, change until the tempo doesn't match previous measure's tempo (next tempo change)
650
650
  const compMeasure = this.score.staves[0].measures[measureIndex];
651
- if (SmoTempoText.eq(originalTempo, compMeasure.tempo) || displayed === false) {
651
+ if (SmoTempo.eq(originalTempo, compMeasure.tempo) || displayed === false) {
652
652
  const sel = SmoSelection.measureSelection(this.score, 0, measureIndex);
653
653
  const altSel = SmoSelection.measureSelection(this.storeScore, 0, measureIndex);
654
654
  if (sel && sel.measure.tempo.display && !displayed) {
@@ -706,17 +706,17 @@ export class SuiScoreViewOperations extends SuiScoreView {
706
706
  * default tempo, or the previously-set tempo.
707
707
  * @param scoreMode whether to reset entire score
708
708
  */
709
- async removeTempo(measure: SmoMeasure, tempo: SmoTempoText, scoreMode: boolean, selectionMode: boolean): Promise<void> {
709
+ async removeTempo(measure: SmoMeasure, tempo: SmoTempo, scoreMode: boolean, selectionMode: boolean): Promise<void> {
710
710
  const startSelection = this.tracker.selections[0];
711
711
  if (startSelection.selector.measure > 0) {
712
712
  const measureIx = startSelection.selector.measure - 1;
713
713
  const target = startSelection.staff.measures[measureIx];
714
714
  const tempo = target.getTempo();
715
- const newTempo = new SmoTempoText(tempo);
715
+ const newTempo = new SmoTempo(tempo);
716
716
  newTempo.display = false;
717
717
  this.updateTempoScore(measure, newTempo, scoreMode, selectionMode);
718
718
  } else {
719
- this.updateTempoScore(measure, new SmoTempoText(SmoTempoText.defaults), scoreMode, selectionMode);
719
+ this.updateTempoScore(measure, new SmoTempo(SmoTempo.defaults), scoreMode, selectionMode);
720
720
  }
721
721
  await this.renderer.updatePromise();
722
722
  }
@@ -850,7 +850,9 @@ export class SuiChordEditor extends SuiTextEditor {
850
850
  if (this.svgText !== null && this.svgText.blocks.length > this.textPos && this.textPos >= 0) {
851
851
  this.textType = this.svgText.blocks[this.textPos].textType;
852
852
  }
853
- evdata.preventDefault(); // prevent browser scroll
853
+ if (typeof(evdata.preventDefault) === 'function') {
854
+ evdata.preventDefault(); // prevent browser scroll
855
+ }
854
856
 
855
857
  return edited;
856
858
  }
@@ -543,15 +543,21 @@ export class VxMeasure implements VxMeasureIf {
543
543
  this.stave = createStave(smoVexStaveParams);
544
544
  if (this.smoMeasure.svg.forceTimeSignature) {
545
545
  const ts = this.smoMeasure.timeSignature;
546
- let tsString = ts.timeSignature;
546
+
547
547
  if (this.smoMeasure.timeSignature.useSymbol && ts.actualBeats === 4 && ts.beatDuration === 4) {
548
- tsString = 'C';
548
+ this.stave.addTimeSignature('C');
549
549
  } else if (this.smoMeasure.timeSignature.useSymbol && ts.actualBeats === 2 && ts.beatDuration === 4) {
550
- tsString = 'C|';
551
- } else if (this.smoMeasure.timeSignature.displayString.length) {
552
- tsString = this.smoMeasure.timeSignature.displayString;
550
+ this.stave.addTimeSignature('C|');
551
+ } else {
552
+ let tsString = this.smoMeasure.timeSignature.displayString.length ? this.smoMeasure.timeSignature.displayString : ts.timeSignature;
553
+ const tsStrings = tsString.split('+');
554
+ for (let i = 0; i < tsStrings.length; i++) {
555
+ const tsString = tsStrings[i].trim();
556
+ if (tsString.length) {
557
+ this.stave.addTimeSignature(tsStrings[i].trim());
558
+ }
559
+ }
553
560
  }
554
- this.stave.addTimeSignature(tsString);
555
561
  }
556
562
  // Connect it to the rendering context and draw!
557
563
  this.stave.setContext(this.context.getContext());