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.
- package/README.md +6 -3
- package/build/smoosic.js +89 -29
- package/changes.md +9 -1
- package/package.json +1 -1
- package/release/html/libmode.html +36 -0
- package/release/html/smoosic.html +4 -4
- package/release/smoosic.js +89 -29
- package/release/styles/dialogs.css +25 -18
- package/release/styles/general.css +16 -9
- package/release/styles/media.css +13 -5
- package/release/styles/ribbon.css +3 -0
- package/src/application/exports.ts +4 -4
- package/src/render/audio/musicCursor.ts +1 -1
- package/src/render/sui/NoteEntryCaret.ts +4 -1
- package/src/render/sui/formatter.ts +3 -3
- package/src/render/sui/mapper.ts +1 -1
- package/src/render/sui/scoreView.ts +4 -4
- package/src/render/sui/scoreViewOperations.ts +10 -10
- package/src/render/sui/textEdit.ts +3 -1
- package/src/render/vex/vxMeasure.ts +12 -6
- package/src/smo/data/measure.ts +37 -37
- package/src/smo/data/measureModifiers.ts +106 -71
- package/src/smo/data/note.ts +4 -1
- package/src/smo/data/score.ts +50 -11
- package/src/smo/data/systemStaff.ts +2 -2
- package/src/smo/midi/midiToSmo.ts +28 -29
- package/src/smo/midi/smoToMidi.ts +3 -3
- package/src/smo/mxml/smoToXml.ts +11 -11
- package/src/smo/mxml/xmlState.ts +3 -3
- package/src/smo/mxml/xmlToSmo.ts +9 -9
- package/src/smo/xform/copypaste.ts +3 -3
- package/src/smo/xform/operations.ts +9 -6
- package/src/smo/xform/tickDuration.ts +10 -2
- package/src/ui/buttons/display.ts +2 -2
- package/src/ui/buttons/ribbon.ts +2 -2
- package/src/ui/components/dialogs/timeSignature.vue +223 -0
- package/src/ui/dialogs/fileDialogs.ts +1 -1
- package/src/ui/dialogs/keySignature.ts +1 -1
- package/src/ui/dialogs/tempo.ts +38 -38
- package/src/ui/dialogs/timeSignature.ts +45 -116
- package/src/ui/menus/timeSignature.ts +2 -2
- package/tools/smoosic-schema.json +4 -4
package/src/smo/data/measure.ts
CHANGED
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
import { smoSerialize } from '../../common/serializationHelpers';
|
|
13
13
|
import { SmoMusic } from './music';
|
|
14
14
|
import {
|
|
15
|
-
SmoBarline, SmoMeasureModifierBase, SmoRepeatSymbol,
|
|
16
|
-
SmoVolta, SmoRehearsalMarkParams, SmoRehearsalMark,
|
|
17
|
-
TimeSignatureParametersSer, SmoMeasureFormatParamsSer,
|
|
15
|
+
SmoBarline, SmoMeasureModifierBase, SmoRepeatSymbol, SmoTempo, SmoMeasureFormat,
|
|
16
|
+
SmoVolta, SmoRehearsalMarkParams, SmoRehearsalMark, SmoTempoParams, SmoTimeSignature,
|
|
17
|
+
TimeSignatureParametersSer, SmoMeasureFormatParamsSer, SmoTempoParamsSer
|
|
18
18
|
} from './measureModifiers';
|
|
19
19
|
import { SmoNote, NoteType, SmoNoteParamsSer } from './note';
|
|
20
20
|
import { SmoTuplet, SmoTupletParamsSer, SmoTupletParams, SmoTupletTreeParamsSer, SmoTupletTree } from './tuplet';
|
|
@@ -148,7 +148,7 @@ export const SmoMeasureStringParams: SmoMeasureStringParam[] = ['keySignature'];
|
|
|
148
148
|
* @category SmoObject
|
|
149
149
|
*/
|
|
150
150
|
export interface SmoMeasureParams {
|
|
151
|
-
timeSignature:
|
|
151
|
+
timeSignature: SmoTimeSignature,
|
|
152
152
|
keySignature: string,
|
|
153
153
|
tupletTrees: SmoTupletTree[],
|
|
154
154
|
transposeIndex: number,
|
|
@@ -158,7 +158,7 @@ export interface SmoMeasureParams {
|
|
|
158
158
|
clef: Clef,
|
|
159
159
|
voices: SmoVoice[],
|
|
160
160
|
activeVoice: number,
|
|
161
|
-
tempo:
|
|
161
|
+
tempo: SmoTempo,
|
|
162
162
|
format: SmoMeasureFormat | null,
|
|
163
163
|
modifiers: SmoMeasureModifierBase[],
|
|
164
164
|
repeatSymbol: boolean,
|
|
@@ -218,7 +218,7 @@ export interface SmoMeasureParamsSer {
|
|
|
218
218
|
/**
|
|
219
219
|
* tempo at this point
|
|
220
220
|
*/
|
|
221
|
-
tempo:
|
|
221
|
+
tempo: SmoTempoParamsSer
|
|
222
222
|
|
|
223
223
|
}
|
|
224
224
|
|
|
@@ -244,8 +244,8 @@ function isSmoMeasureParamsSer(params: Partial<SmoMeasureParamsSer>):params is S
|
|
|
244
244
|
* @category SmoObject
|
|
245
245
|
*/
|
|
246
246
|
export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
247
|
-
static get timeSignatureDefault():
|
|
248
|
-
return new
|
|
247
|
+
static get timeSignatureDefault(): SmoTimeSignature {
|
|
248
|
+
return new SmoTimeSignature(SmoTimeSignature.defaults);
|
|
249
249
|
}
|
|
250
250
|
static defaultDupleDuration: number = 4096;
|
|
251
251
|
static defaultTripleDuration: number = 2048 * 3;
|
|
@@ -268,7 +268,7 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
268
268
|
voices: [],
|
|
269
269
|
format: new SmoMeasureFormat(SmoMeasureFormat.defaults),
|
|
270
270
|
activeVoice: 0,
|
|
271
|
-
tempo: new
|
|
271
|
+
tempo: new SmoTempo(SmoTempo.defaults),
|
|
272
272
|
repeatSymbol: false,
|
|
273
273
|
repeatCount: 0
|
|
274
274
|
}
|
|
@@ -281,7 +281,7 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
281
281
|
static get defaults(): SmoMeasureParams {
|
|
282
282
|
const proto: any = JSON.parse(JSON.stringify(SmoMeasure._defaults));
|
|
283
283
|
proto.format = new SmoMeasureFormat(SmoMeasureFormat.defaults);
|
|
284
|
-
proto.tempo = new
|
|
284
|
+
proto.tempo = new SmoTempo(SmoTempo.defaults);
|
|
285
285
|
proto.modifiers.push(new SmoBarline({
|
|
286
286
|
position: SmoBarline.positions.start,
|
|
287
287
|
barline: SmoBarline.barlines.singleBar
|
|
@@ -294,11 +294,11 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
294
294
|
}
|
|
295
295
|
// @ignore
|
|
296
296
|
static convertLegacyTimeSignature(ts: string) {
|
|
297
|
-
const rv = new
|
|
297
|
+
const rv = new SmoTimeSignature(SmoTimeSignature.defaults);
|
|
298
298
|
rv.timeSignature = ts;
|
|
299
299
|
return rv;
|
|
300
300
|
}
|
|
301
|
-
timeSignature:
|
|
301
|
+
timeSignature: SmoTimeSignature = SmoMeasure.timeSignatureDefault;
|
|
302
302
|
/**
|
|
303
303
|
* Overrides display of actual time signature, in the case of
|
|
304
304
|
* pick-up notes where the actual and displayed durations are different
|
|
@@ -329,7 +329,7 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
329
329
|
* the active voice in the editor, if there are multiple voices
|
|
330
330
|
* */
|
|
331
331
|
activeVoice: number = 0;
|
|
332
|
-
tempo:
|
|
332
|
+
tempo: SmoTempo;
|
|
333
333
|
beamGroups: ISmoBeamGroup[] = [];
|
|
334
334
|
lines: number = 5;
|
|
335
335
|
/**
|
|
@@ -351,7 +351,7 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
351
351
|
* @param params
|
|
352
352
|
*/
|
|
353
353
|
constructor(params: SmoMeasureParams) {
|
|
354
|
-
this.tempo = new
|
|
354
|
+
this.tempo = new SmoTempo(SmoTempo.defaults);
|
|
355
355
|
this.svg = {
|
|
356
356
|
staffWidth: 0,
|
|
357
357
|
unjustifiedWidth: 0,
|
|
@@ -392,7 +392,7 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
392
392
|
this.repeatSymbol = params.repeatSymbol;
|
|
393
393
|
this.measureNumber = JSON.parse(JSON.stringify(params.measureNumber));
|
|
394
394
|
if (params.tempo) {
|
|
395
|
-
this.tempo = new
|
|
395
|
+
this.tempo = new SmoTempo(params.tempo);
|
|
396
396
|
}
|
|
397
397
|
// Handle legacy time signature format
|
|
398
398
|
if (params.timeSignature) {
|
|
@@ -400,7 +400,7 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
400
400
|
if (typeof (tsAny) === 'string') {
|
|
401
401
|
this.timeSignature = SmoMeasure.convertLegacyTimeSignature(tsAny);
|
|
402
402
|
} else {
|
|
403
|
-
this.timeSignature =
|
|
403
|
+
this.timeSignature = SmoTimeSignature.createFromPartial(tsAny);
|
|
404
404
|
}
|
|
405
405
|
}
|
|
406
406
|
this.voices = params.voices ? params.voices : [];
|
|
@@ -454,7 +454,7 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
454
454
|
// Return true if the time signatures are the same, for display purposes (e.g. if a time sig change
|
|
455
455
|
// is required)
|
|
456
456
|
*/
|
|
457
|
-
static timeSigEqual(o1:
|
|
457
|
+
static timeSigEqual(o1: SmoTimeSignature, o2: SmoTimeSignature) {
|
|
458
458
|
return o1.timeSignature === o2.timeSignature && o1.useSymbol === o2.useSymbol;
|
|
459
459
|
}
|
|
460
460
|
/**
|
|
@@ -568,7 +568,7 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
568
568
|
} else if (modifier.ctor === 'SmoBarline' && (modifier as SmoBarline).position === SmoBarline.positions.end
|
|
569
569
|
&& (modifier as SmoBarline).barline === SmoBarline.barlines.singleBar) {
|
|
570
570
|
ser = false;
|
|
571
|
-
} else if (modifier.ctor === '
|
|
571
|
+
} else if (modifier.ctor === 'SmoTempo') {
|
|
572
572
|
// we don't save tempo text as a modifier anymore
|
|
573
573
|
ser = false;
|
|
574
574
|
} else if ((modifier as SmoRepeatSymbol).ctor === 'SmoRepeatSymbol' && (modifier as SmoRepeatSymbol).position === SmoRepeatSymbol.positions.start
|
|
@@ -639,9 +639,9 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
639
639
|
|
|
640
640
|
// explode column-mapped
|
|
641
641
|
if (jsonObj.tempo) {
|
|
642
|
-
params.tempo =
|
|
642
|
+
params.tempo = SmoTempo.deserialize(jsonObj.tempo);
|
|
643
643
|
} else {
|
|
644
|
-
params.tempo = new
|
|
644
|
+
params.tempo = new SmoTempo(SmoTempo.defaults);
|
|
645
645
|
}
|
|
646
646
|
|
|
647
647
|
// timeSignatureString is now part of timeSignature. upconvert old scores
|
|
@@ -654,13 +654,14 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
654
654
|
if (timeSignatureString.length) {
|
|
655
655
|
jsonObj.timeSignature.displayString = timeSignatureString;
|
|
656
656
|
}
|
|
657
|
-
|
|
657
|
+
jsonObj.timeSignature.ctor = 'SmoTimeSignature';
|
|
658
|
+
params.timeSignature = SmoTimeSignature.deserialize(jsonObj.timeSignature);
|
|
658
659
|
} else {
|
|
659
|
-
const tparams =
|
|
660
|
+
const tparams = SmoTimeSignature.defaults;
|
|
660
661
|
if (timeSignatureString.length) {
|
|
661
662
|
tparams.displayString = timeSignatureString;
|
|
662
663
|
}
|
|
663
|
-
params.timeSignature = new
|
|
664
|
+
params.timeSignature = new SmoTimeSignature(tparams);
|
|
664
665
|
}
|
|
665
666
|
params.keySignature = jsonObj.keySignature ?? 'C';
|
|
666
667
|
params.voices = voices;
|
|
@@ -725,12 +726,12 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
725
726
|
const measure = new SmoMeasure(params);
|
|
726
727
|
// Handle migration for measure-mapped parameters
|
|
727
728
|
measure.modifiers.forEach((mod) => {
|
|
728
|
-
if (mod.ctor === '
|
|
729
|
-
measure.tempo = (mod as
|
|
729
|
+
if (mod.ctor === 'SmoTempo') {
|
|
730
|
+
measure.tempo = (mod as SmoTempo);
|
|
730
731
|
}
|
|
731
732
|
});
|
|
732
733
|
if (!measure.tempo) {
|
|
733
|
-
measure.tempo = new
|
|
734
|
+
measure.tempo = new SmoTempo(SmoTempo.defaults);
|
|
734
735
|
}
|
|
735
736
|
|
|
736
737
|
|
|
@@ -747,8 +748,8 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
747
748
|
// Ordinarily, the key/tempo/time is mapped to the stave, but since we are pasting measure-by
|
|
748
749
|
// measure here, we want to preserve it.
|
|
749
750
|
clonedMeasure.keySignature = measure.keySignature;
|
|
750
|
-
clonedMeasure.timeSignature = new
|
|
751
|
-
clonedMeasure.tempo = new
|
|
751
|
+
clonedMeasure.timeSignature = new SmoTimeSignature(measure.timeSignature);
|
|
752
|
+
clonedMeasure.tempo = new SmoTempo(measure.tempo);
|
|
752
753
|
return clonedMeasure;
|
|
753
754
|
}
|
|
754
755
|
hasNonRestNotes(): boolean {
|
|
@@ -834,7 +835,7 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
834
835
|
static get emptyMeasureNoteType(): NoteType {
|
|
835
836
|
return SmoMeasure._emptyMeasureNoteType;
|
|
836
837
|
}
|
|
837
|
-
static timeSignatureNotes(timeSignature:
|
|
838
|
+
static timeSignatureNotes(timeSignature: SmoTimeSignature, clef: Clef) {
|
|
838
839
|
const pitch = SmoMeasure.defaultPitchForClef[clef];
|
|
839
840
|
const maxTicks = SmoMusic.timeSignatureToTicks(timeSignature.timeSignature);
|
|
840
841
|
const noteTick = 8192 / (timeSignature.beatDuration / 2);
|
|
@@ -869,7 +870,7 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
869
870
|
* @returns
|
|
870
871
|
*/
|
|
871
872
|
static getDefaultNotes(params: SmoMeasureParams): SmoNote[] {
|
|
872
|
-
return SmoMeasure.timeSignatureNotes(new
|
|
873
|
+
return SmoMeasure.timeSignatureNotes(new SmoTimeSignature(params.timeSignature), params.clef);
|
|
873
874
|
}
|
|
874
875
|
|
|
875
876
|
/**
|
|
@@ -888,7 +889,7 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
888
889
|
smoSerialize.serializedMerge(SmoMeasure.defaultAttributes, params, obj);
|
|
889
890
|
// Don't copy column-formatting options to new measure in new column
|
|
890
891
|
smoSerialize.serializedMerge(SmoMeasure.formattingOptions, SmoMeasure.defaults, obj);
|
|
891
|
-
obj.timeSignature = new
|
|
892
|
+
obj.timeSignature = new SmoTimeSignature(params.timeSignature);
|
|
892
893
|
// The measure expects to get concert KS in constructor and adjust for instrument. So do the
|
|
893
894
|
// opposite.
|
|
894
895
|
obj.keySignature = SmoMusic.vexKeySigWithOffset(obj.keySignature, -1 * obj.transposeIndex);
|
|
@@ -1019,7 +1020,6 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
1019
1020
|
}
|
|
1020
1021
|
}
|
|
1021
1022
|
const voices: SmoVoice[] = [];
|
|
1022
|
-
const tuplets: SmoTuplet[] = [];
|
|
1023
1023
|
for (var i = 0; i < this.voices.length; ++i) {
|
|
1024
1024
|
const voice = this.voices[i];
|
|
1025
1025
|
const newNotes: SmoNote[] = [];
|
|
@@ -1529,18 +1529,18 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
1529
1529
|
return this.modifiers.filter((mm) => type === mm.attrs.type);
|
|
1530
1530
|
}
|
|
1531
1531
|
|
|
1532
|
-
setTempo(params:
|
|
1533
|
-
this.tempo = new
|
|
1532
|
+
setTempo(params: SmoTempoParams) {
|
|
1533
|
+
this.tempo = new SmoTempo(params);
|
|
1534
1534
|
}
|
|
1535
1535
|
/**
|
|
1536
|
-
* Set measure tempo to the default {@link
|
|
1536
|
+
* Set measure tempo to the default {@link SmoTempo}
|
|
1537
1537
|
*/
|
|
1538
1538
|
resetTempo() {
|
|
1539
|
-
this.tempo = new
|
|
1539
|
+
this.tempo = new SmoTempo(SmoTempo.defaults);
|
|
1540
1540
|
}
|
|
1541
1541
|
getTempo() {
|
|
1542
1542
|
if (typeof (this.tempo) === 'undefined') {
|
|
1543
|
-
this.tempo = new
|
|
1543
|
+
this.tempo = new SmoTempo(SmoTempo.defaults);
|
|
1544
1544
|
}
|
|
1545
1545
|
return this.tempo;
|
|
1546
1546
|
}
|
|
@@ -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
|
|
671
|
+
* constructor parameters for {@link SmoTempo}
|
|
672
672
|
* @category SmoObject
|
|
673
673
|
*/
|
|
674
|
-
export interface
|
|
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
|
|
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
|
|
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():
|
|
754
|
+
static get defaults(): SmoTempoParams {
|
|
755
755
|
return JSON.parse(JSON.stringify({
|
|
756
|
-
tempoMode:
|
|
756
|
+
tempoMode: SmoTempo.tempoModes.durationMode,
|
|
757
757
|
bpm: 120,
|
|
758
758
|
beatDuration: 4096,
|
|
759
|
-
tempoText:
|
|
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 =
|
|
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:
|
|
787
|
+
static eq(t1: SmoTempo, t2: SmoTempo) {
|
|
788
788
|
if (t1.tempoMode !== t2.tempoMode) {
|
|
789
789
|
return false;
|
|
790
790
|
}
|
|
791
|
-
if (t1.tempoMode ===
|
|
791
|
+
if (t1.tempoMode === SmoTempo.tempoModes.durationMode) {
|
|
792
792
|
return t1.bpm === t2.bpm && t1.beatDuration === t2.beatDuration;
|
|
793
793
|
}
|
|
794
|
-
if (t1.tempoMode ===
|
|
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[
|
|
805
|
-
rv[
|
|
806
|
-
rv[
|
|
807
|
-
rv[
|
|
808
|
-
rv[
|
|
809
|
-
rv[
|
|
810
|
-
rv[
|
|
811
|
-
rv[
|
|
812
|
-
rv[
|
|
813
|
-
rv[
|
|
814
|
-
rv[
|
|
815
|
-
rv[
|
|
816
|
-
rv[
|
|
817
|
-
rv[
|
|
818
|
-
rv[
|
|
819
|
-
rv[
|
|
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 ===
|
|
836
|
-
this.tempoMode ===
|
|
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():
|
|
842
|
-
var params: Partial<
|
|
843
|
-
smoSerialize.serializedMergeNonDefault(
|
|
844
|
-
params.ctor = '
|
|
845
|
-
return params as
|
|
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:
|
|
848
|
-
super('
|
|
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(
|
|
854
|
-
smoSerialize.serializedMerge(
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
911
|
-
|
|
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 =
|
|
915
|
-
smoSerialize.serializedMerge(
|
|
916
|
-
return new
|
|
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
|
-
|
|
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
|
-
|
|
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 ['
|
|
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
|
|
935
|
-
this.
|
|
936
|
-
|
|
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(
|
|
941
|
-
rv.ctor = '
|
|
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('
|
|
946
|
-
|
|
947
|
-
|
|
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['
|
|
961
|
-
SmoDynamicCtor['
|
|
995
|
+
SmoDynamicCtor['SmoTempo'] = (params: SmoTempoParams) => new SmoTempo(params);
|
|
996
|
+
SmoDynamicCtor['SmoTimeSignature'] = (params: TimeSignatureParameters) => new SmoTimeSignature(params);
|
|
962
997
|
}
|
package/src/smo/data/note.ts
CHANGED
|
@@ -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'
|