smoosic 1.0.37 → 1.0.39

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 (38) 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/smoosic.js +89 -29
  6. package/release/styles/dialogs.css +4 -0
  7. package/src/application/exports.ts +4 -4
  8. package/src/render/audio/musicCursor.ts +1 -1
  9. package/src/render/sui/NoteEntryCaret.ts +4 -1
  10. package/src/render/sui/formatter.ts +3 -3
  11. package/src/render/sui/mapper.ts +1 -1
  12. package/src/render/sui/scoreView.ts +4 -4
  13. package/src/render/sui/scoreViewOperations.ts +10 -10
  14. package/src/render/sui/textEdit.ts +3 -1
  15. package/src/render/vex/vxMeasure.ts +12 -6
  16. package/src/smo/data/measure.ts +37 -37
  17. package/src/smo/data/measureModifiers.ts +106 -71
  18. package/src/smo/data/note.ts +4 -1
  19. package/src/smo/data/score.ts +50 -11
  20. package/src/smo/data/systemStaff.ts +2 -2
  21. package/src/smo/midi/midiToSmo.ts +28 -29
  22. package/src/smo/midi/smoToMidi.ts +3 -3
  23. package/src/smo/mxml/smoToXml.ts +11 -11
  24. package/src/smo/mxml/xmlState.ts +3 -3
  25. package/src/smo/mxml/xmlToSmo.ts +9 -9
  26. package/src/smo/xform/copypaste.ts +3 -3
  27. package/src/smo/xform/operations.ts +9 -6
  28. package/src/smo/xform/tickDuration.ts +10 -2
  29. package/src/styles/dialogs.css +4 -0
  30. package/src/ui/buttons/display.ts +2 -2
  31. package/src/ui/buttons/ribbon.ts +2 -2
  32. package/src/ui/components/dialogs/timeSignature.vue +223 -0
  33. package/src/ui/dialogs/fileDialogs.ts +1 -1
  34. package/src/ui/dialogs/keySignature.ts +1 -1
  35. package/src/ui/dialogs/tempo.ts +38 -38
  36. package/src/ui/dialogs/timeSignature.ts +45 -116
  37. package/src/ui/menus/timeSignature.ts +2 -2
  38. package/tools/smoosic-schema.json +4 -4
@@ -668,10 +668,10 @@ export type SmoTempoBooleanAttribute = 'display';
668
668
 
669
669
  export type SmoTempoMode = 'duration' | 'text' | 'custom';
670
670
  /**
671
- * constructor parameters for {@link SmoTempoText}
671
+ * constructor parameters for {@link SmoTempo}
672
672
  * @category SmoObject
673
673
  */
674
- export interface SmoTempoTextParams {
674
+ export interface SmoTempoParams {
675
675
  /**
676
676
  * text (e.g. Allegro) or bpm
677
677
  */
@@ -705,7 +705,7 @@ export interface SmoTempoTextParams {
705
705
  * serialized tempo parameters
706
706
  * @category serialization
707
707
  */
708
- export interface SmoTempoTextParamsSer extends SmoTempoTextParams {
708
+ export interface SmoTempoParamsSer extends SmoTempoParams {
709
709
  ctor: string;
710
710
  }
711
711
  /**
@@ -719,7 +719,7 @@ export interface VexTempoTextParams {
719
719
  * Information about both playback tempo and how the tempo is notated.
720
720
  * @category SmoObject
721
721
  */
722
- export class SmoTempoText extends SmoMeasureModifierBase implements SmoTempoTextParams {
722
+ export class SmoTempo extends SmoMeasureModifierBase implements SmoTempoParams {
723
723
  static get tempoModes(): Record<string, SmoTempoMode> {
724
724
  return {
725
725
  durationMode: 'duration',
@@ -751,12 +751,12 @@ export class SmoTempoText extends SmoMeasureModifierBase implements SmoTempoText
751
751
  /**
752
752
  * create defaults for tempo initialization
753
753
  */
754
- static get defaults(): SmoTempoTextParams {
754
+ static get defaults(): SmoTempoParams {
755
755
  return JSON.parse(JSON.stringify({
756
- tempoMode: SmoTempoText.tempoModes.durationMode,
756
+ tempoMode: SmoTempo.tempoModes.durationMode,
757
757
  bpm: 120,
758
758
  beatDuration: 4096,
759
- tempoText: SmoTempoText.tempoTexts.allegro,
759
+ tempoText: SmoTempo.tempoTexts.allegro,
760
760
  yOffset: 0,
761
761
  display: false,
762
762
  customText: ''
@@ -765,7 +765,7 @@ export class SmoTempoText extends SmoMeasureModifierBase implements SmoTempoText
765
765
  static get attributes() {
766
766
  return ['tempoMode', 'bpm', 'display', 'beatDuration', 'tempoText', 'yOffset', 'customText'];
767
767
  }
768
- tempoMode: SmoTempoMode = SmoTempoText.tempoModes.durationMode
768
+ tempoMode: SmoTempoMode = SmoTempo.tempoModes.durationMode
769
769
  bpm: number = 120;
770
770
  beatDuration: number = 4096;
771
771
  tempoText: string = 'Allegro';
@@ -784,14 +784,14 @@ export class SmoTempoText extends SmoMeasureModifierBase implements SmoTempoText
784
784
  * @param t2
785
785
  * @returns
786
786
  */
787
- static eq(t1: SmoTempoText, t2: SmoTempoText) {
787
+ static eq(t1: SmoTempo, t2: SmoTempo) {
788
788
  if (t1.tempoMode !== t2.tempoMode) {
789
789
  return false;
790
790
  }
791
- if (t1.tempoMode === SmoTempoText.tempoModes.durationMode) {
791
+ if (t1.tempoMode === SmoTempo.tempoModes.durationMode) {
792
792
  return t1.bpm === t2.bpm && t1.beatDuration === t2.beatDuration;
793
793
  }
794
- if (t1.tempoMode === SmoTempoText.tempoModes.textMode) {
794
+ if (t1.tempoMode === SmoTempo.tempoModes.textMode) {
795
795
  return t1.tempoText === t2.tempoText;
796
796
  } else {
797
797
  return t1.bpm === t2.bpm && t1.beatDuration === t2.beatDuration &&
@@ -801,22 +801,22 @@ export class SmoTempoText extends SmoMeasureModifierBase implements SmoTempoText
801
801
 
802
802
  static get bpmFromText(): Record<string, number> {
803
803
  const rv: any = {};
804
- rv[SmoTempoText.tempoTexts.larghissimo] = 24;
805
- rv[SmoTempoText.tempoTexts.grave] = 40;
806
- rv[SmoTempoText.tempoTexts.lento] = 45;
807
- rv[SmoTempoText.tempoTexts.largo] = 40;
808
- rv[SmoTempoText.tempoTexts.larghetto] = 60;
809
- rv[SmoTempoText.tempoTexts.adagio] = 72;
810
- rv[SmoTempoText.tempoTexts.adagietto] = 72;
811
- rv[SmoTempoText.tempoTexts.andante_moderato] = 72;
812
- rv[SmoTempoText.tempoTexts.andante] = 84;
813
- rv[SmoTempoText.tempoTexts.andantino] = 92;
814
- rv[SmoTempoText.tempoTexts.moderator] = 96;
815
- rv[SmoTempoText.tempoTexts.allegretto] = 96;
816
- rv[SmoTempoText.tempoTexts.allegro] = 120;
817
- rv[SmoTempoText.tempoTexts.vivace] = 144;
818
- rv[SmoTempoText.tempoTexts.presto] = 168;
819
- rv[SmoTempoText.tempoTexts.prestissimo] = 240;
804
+ rv[SmoTempo.tempoTexts.larghissimo] = 24;
805
+ rv[SmoTempo.tempoTexts.grave] = 40;
806
+ rv[SmoTempo.tempoTexts.lento] = 45;
807
+ rv[SmoTempo.tempoTexts.largo] = 40;
808
+ rv[SmoTempo.tempoTexts.larghetto] = 60;
809
+ rv[SmoTempo.tempoTexts.adagio] = 72;
810
+ rv[SmoTempo.tempoTexts.adagietto] = 72;
811
+ rv[SmoTempo.tempoTexts.andante_moderato] = 72;
812
+ rv[SmoTempo.tempoTexts.andante] = 84;
813
+ rv[SmoTempo.tempoTexts.andantino] = 92;
814
+ rv[SmoTempo.tempoTexts.moderator] = 96;
815
+ rv[SmoTempo.tempoTexts.allegretto] = 96;
816
+ rv[SmoTempo.tempoTexts.allegro] = 120;
817
+ rv[SmoTempo.tempoTexts.vivace] = 144;
818
+ rv[SmoTempo.tempoTexts.presto] = 168;
819
+ rv[SmoTempo.tempoTexts.prestissimo] = 240;
820
820
 
821
821
  return rv as Record<string, number>;
822
822
  }
@@ -832,29 +832,32 @@ export class SmoTempoText extends SmoMeasureModifierBase implements SmoTempoText
832
832
  return rv;
833
833
  }
834
834
  toVexTempo(): VexTempoTextParams {
835
- if (this.tempoMode === SmoTempoText.tempoModes.durationMode ||
836
- this.tempoMode === SmoTempoText.tempoModes.customMode) {
835
+ if (this.tempoMode === SmoTempo.tempoModes.durationMode ||
836
+ this.tempoMode === SmoTempo.tempoModes.customMode) {
837
837
  return this._toVexDurationTempo();
838
838
  }
839
839
  return this._toVexTextTempo();
840
840
  }
841
- serialize(): SmoTempoTextParamsSer {
842
- var params: Partial<SmoTempoTextParamsSer> = {};
843
- smoSerialize.serializedMergeNonDefault(SmoTempoText.defaults, SmoTempoText.attributes, this, params);
844
- params.ctor = 'SmoTempoText';
845
- return params as SmoTempoTextParamsSer;
841
+ serialize(): SmoTempoParamsSer {
842
+ var params: Partial<SmoTempoParamsSer> = {};
843
+ smoSerialize.serializedMergeNonDefault(SmoTempo.defaults, SmoTempo.attributes, this, params);
844
+ params.ctor = 'SmoTempo';
845
+ return params as SmoTempoParamsSer;
846
846
  }
847
- constructor(parameters: SmoTempoTextParams | null) {
848
- super('SmoTempoText');
847
+ constructor(parameters: SmoTempoParams | null) {
848
+ super('SmoTempo');
849
849
  let pobj: any = parameters;
850
850
  if (typeof (pobj) === 'undefined' || pobj === null) {
851
851
  pobj = {};
852
852
  }
853
- smoSerialize.serializedMerge(SmoTempoText.attributes, SmoTempoText.defaults, this);
854
- smoSerialize.serializedMerge(SmoTempoText.attributes, pobj, this);
853
+ smoSerialize.serializedMerge(SmoTempo.attributes, SmoTempo.defaults, this);
854
+ smoSerialize.serializedMerge(SmoTempo.attributes, pobj, this);
855
855
  }
856
856
  }
857
-
857
+ export interface TimeSignatureTime {
858
+ actualBeats: number,
859
+ beatDuration: number
860
+ }
858
861
  /**
859
862
  * Constructor parameters for a time signature
860
863
  * @category SmoObject
@@ -863,14 +866,7 @@ export interface TimeSignatureParameters {
863
866
  /**
864
867
  * numerator
865
868
  */
866
- actualBeats: number,
867
- /**
868
- * denominator, always power of 2
869
- */
870
- beatDuration: number,
871
- /**
872
- * indicates cut time/common time
873
- */
869
+ times: TimeSignatureTime[],
874
870
  useSymbol: boolean,
875
871
  /**
876
872
  * display, else just affects measure lengths.
@@ -879,7 +875,7 @@ export interface TimeSignatureParameters {
879
875
  /**
880
876
  * for pickups, display the non-pickup value
881
877
  */
882
- displayString: string
878
+ displayString: string,
883
879
  }
884
880
 
885
881
  /**
@@ -897,54 +893,93 @@ export interface TimeSignatureParametersSer extends TimeSignatureParameters {
897
893
  * about the display of the time signature.
898
894
  * @category SmoObject
899
895
  */
900
- export class TimeSignature extends SmoMeasureModifierBase {
896
+ export class SmoTimeSignature extends SmoMeasureModifierBase {
901
897
  static get defaults(): TimeSignatureParameters {
902
898
  return {
903
- actualBeats: 4,
904
- beatDuration: 4,
899
+ times: [{ actualBeats: 4, beatDuration: 4 }],
905
900
  useSymbol: false,
906
901
  display: true,
907
- displayString: ''
902
+ displayString: '',
908
903
  };
909
904
  }
910
- static equal(ts1: TimeSignature, ts2: TimeSignature): boolean {
911
- return (ts1.actualBeats === ts2.actualBeats && ts1.beatDuration === ts2.beatDuration);
905
+ ticksFromTimeSignature() {
906
+ let max = 0;
907
+ for (let i = 0; i < this.times.length; i++) {
908
+ const bt = 4096 * (4/this.times[i].beatDuration);
909
+ max = Math.max(max, bt * this.times[i].actualBeats);
910
+ }
911
+ return max;
912
+ }
913
+ static equal(ts1: SmoTimeSignature, ts2: SmoTimeSignature): boolean {
914
+ // legacy hack, this can be called before the constructor is called, since these classes used to
915
+ // just be strings. So create the objects.
916
+ const ts1obj = SmoTimeSignature.createFromPartial(ts1);
917
+ const ts2obj = SmoTimeSignature.createFromPartial(ts2);
918
+ if (ts1obj.times.length !== ts2obj.times.length) {
919
+ return false;
920
+ }
921
+ for (let i = 0; i < ts1obj.times.length; i++) {
922
+ if (ts1obj.times[i].actualBeats !== ts2obj.times[i].actualBeats ||
923
+ ts1obj.times[i].beatDuration !== ts2obj.times[i].beatDuration) {
924
+ return false;
925
+ }
926
+ }
927
+ return true;
912
928
  }
913
929
  static createFromPartial(value: Partial<TimeSignatureParameters>) {
914
- const params = TimeSignature.defaults;
915
- smoSerialize.serializedMerge(TimeSignature.parameters, value, params);
916
- return new TimeSignature(params);
930
+ const params = SmoTimeSignature.defaults;
931
+ smoSerialize.serializedMerge(SmoTimeSignature.parameters, value, params);
932
+ return new SmoTimeSignature(params);
917
933
  }
918
934
  // timeSignature: string = '4/4';
919
- actualBeats: number = 4;
920
- beatDuration: number = 4;
935
+ times: TimeSignatureTime[] = [{ actualBeats: 4, beatDuration: 4 }];
921
936
  useSymbol: boolean = false;
922
937
  display: boolean = true;
923
938
  displayString: string = '';
939
+ index: number = 0;
940
+ get beatDuration() {
941
+ return this.times[this.index].beatDuration;
942
+ }
943
+ get actualBeats() { return this.times[this.index].actualBeats;
944
+ }
924
945
  get timeSignature() {
925
- return this.actualBeats.toString() + '/' + this.beatDuration.toString();
946
+ let str: string[] = [];
947
+ this.times.forEach((time) => {
948
+ str.push(`${time.actualBeats}/${time.beatDuration}+`);
949
+ });
950
+ return str.join('+');
926
951
  }
927
952
  static get parameters() {
928
- return ['actualBeats', 'beatDuration', 'useSymbol', 'display', 'displayString'];
953
+ return ['times', 'useSymbol', 'display', 'displayString'];
929
954
  }
930
955
  static get boolParameters() {
931
956
  return [];
932
957
  }
933
958
  set timeSignature(value: string) {
934
- const ar = value.split('/');
935
- this.actualBeats = parseInt(ar[0], 10);
936
- this.beatDuration = parseInt(ar[1], 10);
959
+ const timeStrings = value.split('+');
960
+ this.times = [];
961
+ for (let i = 0; i < timeStrings.length; i++) {
962
+ const ar = timeStrings[i].split('/');
963
+ if (ar.length !== 2) {
964
+ throw new Error('invalid time signature string');
965
+ }
966
+ const actualBeats = parseInt(ar[0], 10);
967
+ const beatDuration = parseInt(ar[1], 10);
968
+ this.times.push({ actualBeats, beatDuration });
969
+ }
937
970
  }
938
971
  serialize(): TimeSignatureParametersSer {
939
972
  const rv: Partial<TimeSignatureParametersSer> = {};
940
- smoSerialize.serializedMergeNonDefault(TimeSignature.defaults, TimeSignature.parameters, this, rv);
941
- rv.ctor = 'TimeSignature';
973
+ smoSerialize.serializedMergeNonDefault(SmoTimeSignature.defaults, SmoTimeSignature.parameters, this, rv);
974
+ rv.ctor = 'SmoTimeSignature';
942
975
  return rv as TimeSignatureParametersSer;
943
976
  }
944
977
  constructor(params: TimeSignatureParameters) {
945
- super('TimeSignature');
946
- this.actualBeats = params.actualBeats;
947
- this.beatDuration = params.beatDuration;
978
+ super('SmoTimeSignature');
979
+ if (!params.times || !params.times.length) {
980
+ params.times = SmoTimeSignature.defaults.times;
981
+ }
982
+ this.times = JSON.parse(JSON.stringify(params.times));
948
983
  this.useSymbol = params.useSymbol;
949
984
  this.display = params.display;
950
985
  this.displayString = params.displayString;
@@ -957,6 +992,6 @@ export const measureModifierDynamicCtorInit = () => {
957
992
  SmoDynamicCtor['SmoVolta'] = (params: SmoVoltaParams) => new SmoVolta(params);
958
993
  SmoDynamicCtor['SmoMeasureText'] = (params: SmoMeasureTextParams) => new SmoMeasureText(params);
959
994
  SmoDynamicCtor['SmoRehearsalMark'] = (params: SmoRehearsalMarkParams) => new SmoRehearsalMark(params);
960
- SmoDynamicCtor['SmoTempoText'] = (params: SmoTempoTextParams) => new SmoTempoText(params);
961
- SmoDynamicCtor['TimeSignature'] = (params: TimeSignatureParameters) => new TimeSignature(params);
995
+ SmoDynamicCtor['SmoTempo'] = (params: SmoTempoParams) => new SmoTempo(params);
996
+ SmoDynamicCtor['SmoTimeSignature'] = (params: TimeSignatureParameters) => new SmoTimeSignature(params);
962
997
  }
@@ -312,7 +312,10 @@ export class SmoNote implements Transposable {
312
312
  if ((params as any).tuplet) {
313
313
  this.tupletId = (params as any).tuplet.id;
314
314
  }
315
-
315
+ // For imported notes, stem ticks is only different for tuplets
316
+ if (!this.isTuplet) {
317
+ this.stemTicks = this.tickCount;
318
+ }
316
319
  this.attrs = {
317
320
  id: getId().toString(),
318
321
  type: 'SmoNote'
@@ -9,7 +9,7 @@ import { Clef, SvgDimensions } from './common';
9
9
  import { SmoMeasure, SmoMeasureParams, ColumnMappedParams, SmoMeasureParamsSer } from './measure';
10
10
  import { SmoNoteModifierBase } from './noteModifiers';
11
11
  import {
12
- SmoTempoText, SmoMeasureFormat, SmoMeasureModifierBase, TimeSignature, TimeSignatureParameters,
12
+ SmoTempo, SmoMeasureFormat, SmoMeasureModifierBase, SmoTimeSignature, TimeSignatureParameters,
13
13
  SmoMeasureFormatParamsSer
14
14
  } from './measureModifiers';
15
15
  import { StaffModifierBase, SmoInstrument } from './staffModifiers';
@@ -153,8 +153,8 @@ export function isEmptyTextBlock(params: Partial<SmoTextGroupParamsSer>): params
153
153
  */
154
154
  export interface ColumnParamsMapType {
155
155
  keySignature: Record<number, string>,
156
- tempo: Record<number, SmoTempoText>,
157
- timeSignature: Record<number, TimeSignature>,
156
+ tempo: Record<number, SmoTempo>,
157
+ timeSignature: Record<number, SmoTimeSignature>,
158
158
  renumberingMap: Record<number, number>
159
159
  }
160
160
 
@@ -351,8 +351,8 @@ export class SmoScore {
351
351
  */
352
352
  serializeColumnMapped(func: (measure: SmoMeasure) => ColumnMappedParams) {
353
353
  const keySignature: Record<number, string> = {};
354
- const tempo: Record<number, SmoTempoText> = {};
355
- const timeSignature: Record<number, TimeSignature> = {};
354
+ const tempo: Record<number, SmoTempo> = {};
355
+ const timeSignature: Record<number, SmoTimeSignature> = {};
356
356
  const renumberingMap: Record<number, number> = {};
357
357
  let previous: ColumnMappedParams | null = null;
358
358
  this.staves[0].measures.forEach((measure) => {
@@ -381,11 +381,11 @@ export class SmoScore {
381
381
  previous!.keySignature = current.keySignature;
382
382
  keySignature[ix] = current.keySignature;
383
383
  }
384
- if (!(TimeSignature.equal(current.timeSignature, previous!.timeSignature))) {
384
+ if (!(SmoTimeSignature.equal(current.timeSignature, previous!.timeSignature))) {
385
385
  previous!.timeSignature = current.timeSignature;
386
386
  timeSignature[ix] = current.timeSignature;
387
387
  }
388
- if (!(SmoTempoText.eq(current.tempo, previous!.tempo))) {
388
+ if (!(SmoTempo.eq(current.tempo, previous!.tempo))) {
389
389
  previous!.tempo = current.tempo;
390
390
  tempo[ix] = current.tempo;
391
391
  }
@@ -393,7 +393,45 @@ export class SmoScore {
393
393
  });
394
394
  return { keySignature, tempo, timeSignature, renumberingMap };
395
395
  }
396
-
396
+ static fixLegacyColumnMappedCtor(scoreObj: any){
397
+ if (!scoreObj.columnAttributeMap) {
398
+ return;
399
+ }
400
+ if (!scoreObj.columnAttributeMap.tempo) {
401
+ return;
402
+ }
403
+ const tempoKeys = Object.keys(scoreObj.columnAttributeMap.tempo);
404
+ tempoKeys.forEach((key) => {
405
+ const t = scoreObj.columnAttributeMap.tempo[key];
406
+ t.ctor = 'SmoTempo';
407
+ });
408
+ const tsKeys = Object.keys(scoreObj.columnAttributeMap.timeSignature);
409
+ tsKeys.forEach((key) => {
410
+ const val = scoreObj.columnAttributeMap.timeSignature[key];
411
+ // Very legacy time signature format was just a string like 4/4, 3/8 etc.
412
+ if (typeof(val) === 'string') {
413
+ const nums = val.split('/');
414
+ const num = parseInt(nums[0], 10);
415
+ const denom = parseInt(nums[1], 10);
416
+ scoreObj.columnAttributeMap.timeSignature[key] = { times: [{ actualBeats: num, beatDuration: denom }], ctor: 'SmoTimeSignature' };
417
+ } else {
418
+ const ts = val;
419
+ ts.ctor = 'SmoTimeSignature';
420
+ // map legacy time signature format to compound time signature format
421
+ let beatDuration = 4;
422
+ if (ts.beatDuration && !isNaN(ts.beatDuration)) {
423
+ beatDuration = ts.beatDuration;
424
+ }
425
+ if (ts.actualBeats && !isNaN(ts.actualBeats)) {
426
+ ts.times = [{ actualBeats: ts.actualBeats, beatDuration }];
427
+ }
428
+ // Default will have nothing serialized
429
+ if (!ts.times) {
430
+ ts.times = [{ actualBeats: 4, beatDuration: 4 }];
431
+ }
432
+ }
433
+ });
434
+ }
397
435
  /**
398
436
  * Column-mapped attributes stay the same in each measure until
399
437
  * changed, like key-signatures. We don't store each measure value to
@@ -407,6 +445,7 @@ export class SmoScore {
407
445
  if (!scoreObj.columnAttributeMap) {
408
446
  return;
409
447
  }
448
+ SmoScore.fixLegacyColumnMappedCtor(scoreObj);
410
449
  const attrs = Object.keys(scoreObj.columnAttributeMap);
411
450
  scoreObj.staves.forEach((staff: any) => {
412
451
  const attrIxMap: any = {};
@@ -429,12 +468,12 @@ export class SmoScore {
429
468
  }
430
469
  // legacy timeSignature format was just a string 2/4, 3/8 etc.
431
470
  if (attr === 'timeSignature') {
432
- const ts = new TimeSignature(TimeSignature.defaults);
471
+ const ts = new SmoTimeSignature(SmoTimeSignature.defaults);
433
472
  if (typeof (curValue) === 'string') {
434
473
  ts.timeSignature = curValue;
435
474
  measure[attr] = ts;
436
475
  } else {
437
- measure[attr] = TimeSignature.createFromPartial(curValue);
476
+ measure[attr] = SmoTimeSignature.createFromPartial(curValue);
438
477
  }
439
478
  } else {
440
479
  measure[attr] = curValue;
@@ -878,7 +917,7 @@ export class SmoScore {
878
917
  if (measureIndex > 0) {
879
918
  const measure = this.staves[0].measures[measureIndex];
880
919
  const prev = this.staves[0].measures[measureIndex - 1];
881
- if (!start && !TimeSignature.equal(measure.timeSignature, prev.timeSignature)) {
920
+ if (!start && !SmoTimeSignature.equal(measure.timeSignature, prev.timeSignature)) {
882
921
  return false;
883
922
  }
884
923
  if (!start && measure.keySignature !== prev.keySignature) {
@@ -9,7 +9,7 @@ import { SmoObjectParams, SmoAttrs, MeasureNumber, getId,
9
9
  Pitch, ElementLike } from './common';
10
10
  import { SmoMusic } from './music';
11
11
  import { SmoMeasure, SmoMeasureParamsSer } from './measure';
12
- import { SmoMeasureFormat, SmoRehearsalMark, SmoRehearsalMarkParams, SmoTempoTextParams, SmoVolta, SmoBarline } from './measureModifiers';
12
+ import { SmoMeasureFormat, SmoRehearsalMark, SmoRehearsalMarkParams, SmoTempoParams, SmoVolta, SmoBarline } from './measureModifiers';
13
13
  import { SmoInstrumentParams, StaffModifierBase, SmoInstrument, SmoInstrumentMeasure, SmoInstrumentStringParams, SmoInstrumentNumParams,
14
14
  SmoTie, SmoStaffTextBracket, SmoStaffTextBracketParamsSer,
15
15
  StaffModifierBaseSer, SmoTabStave, SmoStaffHairpin, TieLine,
@@ -851,7 +851,7 @@ export class SmoSystemStaff implements SmoObjectParams {
851
851
  this.measures[index].resetTempo();
852
852
  }
853
853
 
854
- addTempo(tempo: SmoTempoTextParams, index: number) {
854
+ addTempo(tempo: SmoTempoParams, index: number) {
855
855
  this.measures[index].setTempo(tempo);
856
856
  }
857
857
 
@@ -7,7 +7,7 @@
7
7
  import { SmoToMidi } from "./smoToMidi";
8
8
  import { Clef, Pitch } from "../data/common";
9
9
  import { SmoMeasure } from "../data/measure";
10
- import { SmoTempoText, TimeSignature } from "../data/measureModifiers";
10
+ import { SmoTempo, SmoTimeSignature } from "../data/measureModifiers";
11
11
  import { SmoMusic } from "../data/music";
12
12
  import { SmoNote } from "../data/note";
13
13
  import { SmoScore } from "../data/score";
@@ -18,7 +18,7 @@ import {SmoTuplet, SmoTupletTree} from "../data/tuplet";
18
18
  import { SmoOperation } from "../xform/operations";
19
19
 
20
20
  export type MidiEventType = 'text' | 'copyrightNotice' | 'trackName' | 'instrumentName' | 'lyrics' | 'marker' |
21
- 'cuePoint' | 'channelPrefix' | 'portPrefix' | 'endOfTrack' | 'setTempo' | 'smpteOffset' | 'timeSignature' | 'keySignature' |
21
+ 'cuePoint' | 'channelPrefix' | 'portPrefix' | 'endOfTrack' | 'setTempo' | 'smpteOffset' | 'SmoTimeSignature' | 'keySignature' |
22
22
  'sequencerSpecific' | 'unknownMeta' |
23
23
  'noteOff' | 'noteOn' | 'noteAftertouch' | 'controller' | 'programChange' | 'channelAftertouch' | 'pitchBend';
24
24
 
@@ -46,8 +46,8 @@ export interface MidiTrackEvent {
46
46
  */
47
47
  export interface RunningMetadata {
48
48
  keySignature: string,
49
- timeSignature: TimeSignature,
50
- tempo: SmoTempoText
49
+ timeSignature: SmoTimeSignature,
50
+ tempo: SmoTempo
51
51
  }
52
52
  /**
53
53
  * @category serialization
@@ -75,8 +75,8 @@ export interface EventSmoData {
75
75
  tupletInfo: MidiTupletInfo | null,
76
76
  isRest: boolean,
77
77
  isTied: boolean,
78
- timeSignature: TimeSignature,
79
- tempo: SmoTempoText,
78
+ timeSignature: SmoTimeSignature,
79
+ tempo: SmoTempo,
80
80
  keySignature: string,
81
81
  measure: number,
82
82
  tick: number
@@ -100,8 +100,8 @@ export function getValueForTick<T>(arg: Record<number, T>, tick: number) {
100
100
  * @category serialization
101
101
  */
102
102
  export class MidiToSmo {
103
- timeSignatureMap: Record<number, TimeSignature> = {};
104
- tempoMap: Record<number, SmoTempoText> = {};
103
+ SmoTimeSignatureMap: Record<number, SmoTimeSignature> = {};
104
+ tempoMap: Record<number, SmoTempo> = {};
105
105
  keySignatureMap: Record<number, string> = {};
106
106
  tieMap: Record<number, number[]> = {};
107
107
  timeDivision: number = 480;
@@ -153,8 +153,8 @@ export class MidiToSmo {
153
153
  constructor(midi: any, quantizeDuration: number) {
154
154
  this.midi = midi;
155
155
  // console.log(JSON.stringify(midi, null, ''));
156
- this.timeSignatureMap[0] = new TimeSignature(TimeSignature.defaults);
157
- this.tempoMap[0] = new SmoTempoText(SmoTempoText.defaults);
156
+ this.SmoTimeSignatureMap[0] = new SmoTimeSignature(SmoTimeSignature.defaults);
157
+ this.tempoMap[0] = new SmoTempo(SmoTempo.defaults);
158
158
  this.keySignatureMap[0] = 'c';
159
159
  this.timeDivision = midi.header.ticksPerBeat;
160
160
  this.quantizeTicks = quantizeDuration;
@@ -175,11 +175,11 @@ export class MidiToSmo {
175
175
  * @param ticks
176
176
  * @returns
177
177
  */
178
- getTimeSignature(ticks: number): TimeSignature {
179
- if (this.timeSignatureMap[ticks]) {
180
- return this.timeSignatureMap[ticks];
178
+ getSmoTimeSignature(ticks: number): SmoTimeSignature {
179
+ if (this.SmoTimeSignatureMap[ticks]) {
180
+ return this.SmoTimeSignatureMap[ticks];
181
181
  }
182
- return getValueForTick(this.timeSignatureMap, ticks);
182
+ return getValueForTick(this.SmoTimeSignatureMap, ticks);
183
183
  }
184
184
  /**
185
185
  * @internal
@@ -199,7 +199,7 @@ export class MidiToSmo {
199
199
  */
200
200
  getMetadata(ticks: number) {
201
201
  return { tempo: this.getTempo(ticks),
202
- timeSignature: this.getTimeSignature(ticks), keySignature: this.getKeySignature(ticks).toLowerCase() };
202
+ timeSignature: this.getSmoTimeSignature(ticks), keySignature: this.getKeySignature(ticks).toLowerCase() };
203
203
  }
204
204
  /**
205
205
  * We process 3 types of metadata at present: time signature, tempo and keysignature.
@@ -209,22 +209,21 @@ export class MidiToSmo {
209
209
  handleMetadata(trackEvent: MidiTrackEvent, ticks: number) {
210
210
  if (trackEvent.meta) {
211
211
  const mtype = trackEvent.type;
212
- if (mtype === 'timeSignature') {
212
+ if (mtype === 'SmoTimeSignature') {
213
213
  /**
214
214
  * whenever we get a time signature event, recompute ticks per measure
215
215
  */
216
216
  const numerator = trackEvent.numerator!;
217
217
  const denominator = trackEvent.denominator!;
218
- const tsDef = TimeSignature.defaults;
219
- tsDef.actualBeats = numerator;
220
- tsDef.beatDuration = denominator;
221
- const ts = new TimeSignature(tsDef);
222
- this.timeSignatureMap[ticks] = ts;
218
+ const tsDef = SmoTimeSignature.defaults;
219
+ tsDef.times.push({actualBeats: numerator, beatDuration:denominator});
220
+ const ts = new SmoTimeSignature(tsDef);
221
+ this.SmoTimeSignatureMap[ticks] = ts;
223
222
  } else if (mtype === 'setTempo') {
224
223
  const bpm = 60 / (trackEvent.microsecondsPerBeat! / 1000000);
225
- const tempoDef = SmoTempoText.defaults;
224
+ const tempoDef = SmoTempo.defaults;
226
225
  tempoDef.bpm = bpm;
227
- this.tempoMap[ticks] = new SmoTempoText(tempoDef);
226
+ this.tempoMap[ticks] = new SmoTempo(tempoDef);
228
227
  } else if (mtype === 'keySignature') {
229
228
  const mdata = trackEvent.key!;
230
229
  if (mdata === 0) {
@@ -255,8 +254,8 @@ export class MidiToSmo {
255
254
  */
256
255
  createNewEvent(metadata: RunningMetadata): EventSmoData {
257
256
  return {
258
- pitches: [], durationTicks: 0, tupletInfo: null, isRest: false, timeSignature: new TimeSignature(metadata.timeSignature),
259
- tempo: new SmoTempoText(metadata.tempo), keySignature: metadata.keySignature, measure: 0, tick: 0, isTied: false
257
+ pitches: [], durationTicks: 0, tupletInfo: null, isRest: false, timeSignature: new SmoTimeSignature(metadata.timeSignature),
258
+ tempo: new SmoTempo(metadata.tempo), keySignature: metadata.keySignature, measure: 0, tick: 0, isTied: false
260
259
  };
261
260
  }
262
261
  /**
@@ -264,8 +263,8 @@ export class MidiToSmo {
264
263
  */
265
264
  static copyEvent(o: EventSmoData): EventSmoData {
266
265
  const pitches = JSON.parse(JSON.stringify(o.pitches));
267
- const timeSignature = new TimeSignature(o.timeSignature);
268
- const tempo = new SmoTempoText(o.tempo);
266
+ const timeSignature = new SmoTimeSignature(o.timeSignature);
267
+ const tempo = new SmoTempo(o.tempo);
269
268
  return ({
270
269
  pitches, durationTicks: o.durationTicks, tupletInfo: o.tupletInfo, isRest: o.isRest, timeSignature, tempo, keySignature: o.keySignature,
271
270
  measure: o.measure, tick: o.tick, isTied: o.isTied
@@ -298,8 +297,8 @@ export class MidiToSmo {
298
297
  if (measure === null || ev.measure > measureIndex) {
299
298
  const measureDefs = SmoMeasure.defaults;
300
299
  measureDefs.keySignature = ev.keySignature;
301
- measureDefs.timeSignature = new TimeSignature(ev.timeSignature);
302
- measureDefs.tempo = new SmoTempoText(ev.tempo);
300
+ measureDefs.timeSignature = new SmoTimeSignature(ev.timeSignature);
301
+ measureDefs.tempo = new SmoTempo(ev.tempo);
303
302
  measure = new SmoMeasure(measureDefs);
304
303
  measure.voices.push({ notes: [] });
305
304
  measureIndex = ev.measure;
@@ -5,7 +5,7 @@
5
5
  * @module /smo/midi/smoToMidi
6
6
  */
7
7
  import { SmoMusic } from '../data/music';
8
- import { TimeSignature, SmoTempoText } from '../data/measureModifiers';
8
+ import { SmoTimeSignature, SmoTempo } from '../data/measureModifiers';
9
9
  import { ScoreRoadMapBuilder } from '../xform/roadmap';
10
10
  import { SmoScore } from '../data/score';
11
11
  import { PopulateAudioData } from '../xform/updateAudio';
@@ -67,8 +67,8 @@ import Writer from 'midi-writer-js';
67
67
  export interface MidiTrackHash {
68
68
  track: any,
69
69
  lastMeasure: number,
70
- timeSignature?: TimeSignature,
71
- tempo?: SmoTempoText,
70
+ timeSignature?: SmoTimeSignature,
71
+ tempo?: SmoTempo,
72
72
  keySignature?: string
73
73
  }
74
74
  function stavesToSkip(score: SmoScore): number[] {