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.
- package/README.md +6 -3
- package/build/smoosic.js +89 -29
- package/changes.md +9 -1
- package/package.json +1 -1
- package/release/smoosic.js +89 -29
- package/release/styles/dialogs.css +4 -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/styles/dialogs.css +4 -0
- 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/mxml/smoToXml.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { SmoMusic } from '../data/music';
|
|
|
6
6
|
import { SmoMeasure, SmoVoice } from '../data/measure';
|
|
7
7
|
import { SmoSystemStaff } from '../data/systemStaff';
|
|
8
8
|
import { SmoScore } from '../data/score';
|
|
9
|
-
import { SmoBarline,
|
|
9
|
+
import { SmoBarline, SmoTimeSignature, SmoRehearsalMark, SmoMeasureModifierBase } from '../data/measureModifiers';
|
|
10
10
|
import { SmoStaffHairpin, SmoSlur, SmoTie } from '../data/staffModifiers';
|
|
11
11
|
import { SmoArticulation, SmoLyric, SmoOrnament } from '../data/noteModifiers';
|
|
12
12
|
import { SmoSelector } from '../xform/selections';
|
|
@@ -14,7 +14,7 @@ import { SmoTuplet, SmoTupletTree } from '../data/tuplet';
|
|
|
14
14
|
|
|
15
15
|
import { XmlHelpers } from './xmlHelpers';
|
|
16
16
|
import { smoSerialize } from '../../common/serializationHelpers';
|
|
17
|
-
import {
|
|
17
|
+
import { SmoTempo } from '../data/measureModifiers';
|
|
18
18
|
import { XmlToSmo } from './xmlToSmo';
|
|
19
19
|
import { SmoSystemGroup } from '../data/scoreModifiers';
|
|
20
20
|
import { SuiSampleMedia } from '../../render/audio/samples';
|
|
@@ -51,8 +51,8 @@ export interface SmoState {
|
|
|
51
51
|
note?: SmoNote,
|
|
52
52
|
beamState: number,
|
|
53
53
|
beamTicks: number,
|
|
54
|
-
timeSignature?:
|
|
55
|
-
tempo?:
|
|
54
|
+
timeSignature?: SmoTimeSignature,
|
|
55
|
+
tempo?: SmoTempo,
|
|
56
56
|
currentTupletLevel: number, // not sure about the name
|
|
57
57
|
}
|
|
58
58
|
|
|
@@ -637,13 +637,13 @@ export class SmoToXml {
|
|
|
637
637
|
if (smoState.tempo) {
|
|
638
638
|
if (tempo.display && measure.measureNumber.measureIndex === 0 && smoState.measureTicks === 0) {
|
|
639
639
|
displayTempo = true;
|
|
640
|
-
} else if (tempo.display && !
|
|
640
|
+
} else if (tempo.display && !SmoTempo.eq(smoState.tempo, tempo)) {
|
|
641
641
|
displayTempo = true;
|
|
642
642
|
}
|
|
643
643
|
} else {
|
|
644
644
|
displayTempo = true;
|
|
645
645
|
}
|
|
646
|
-
smoState.tempo = new
|
|
646
|
+
smoState.tempo = new SmoTempo(tempo);
|
|
647
647
|
if (beforeNote === true && smoState.staffPartIx === 0 && smoState.measureTicks === 0 && smoState.partStaves[0].staffId === 0) {
|
|
648
648
|
const mark: SmoMeasureModifierBase | undefined = measure.getRehearsalMark();
|
|
649
649
|
if (mark) {
|
|
@@ -661,12 +661,12 @@ export class SmoToXml {
|
|
|
661
661
|
const tempoElement = nn(directionElement, 'direction-type', null, '');
|
|
662
662
|
XmlHelpers.createAttribute(directionElement, 'placement', 'above');
|
|
663
663
|
let tempoText = tempo.tempoText;
|
|
664
|
-
if (tempo.tempoMode ===
|
|
664
|
+
if (tempo.tempoMode === SmoTempo.tempoModes.customMode) {
|
|
665
665
|
tempoText = tempo.customText;
|
|
666
666
|
}
|
|
667
|
-
if (tempo.tempoMode ===
|
|
667
|
+
if (tempo.tempoMode === SmoTempo.tempoModes.textMode) {
|
|
668
668
|
nn(tempoElement, 'words', { words: tempoText }, 'words');
|
|
669
|
-
} else if (tempo.tempoMode ===
|
|
669
|
+
} else if (tempo.tempoMode === SmoTempo.tempoModes.customMode || tempo.tempoMode === SmoTempo.tempoModes.durationMode) {
|
|
670
670
|
const metronomeElement = nn(tempoElement, 'metronome', null, '');
|
|
671
671
|
let durationType = 'quarter';
|
|
672
672
|
let dotType = false;
|
|
@@ -901,8 +901,8 @@ export class SmoToXml {
|
|
|
901
901
|
const nn = XmlHelpers.createTextElementChild;
|
|
902
902
|
const staff = smoState.partStaves[smoState.staffPartIx];
|
|
903
903
|
const measure = staff.measures[smoState.measureIndex];
|
|
904
|
-
const currentTs = (smoState.timeSignature as
|
|
905
|
-
if (currentTs !== null &&
|
|
904
|
+
const currentTs = (smoState.timeSignature as SmoTimeSignature) ?? null;
|
|
905
|
+
if (currentTs !== null && SmoTimeSignature.equal(currentTs, measure.timeSignature)) {
|
|
906
906
|
return;
|
|
907
907
|
}
|
|
908
908
|
smoState.timeSignature = measure.timeSignature;
|
package/src/smo/mxml/xmlState.ts
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
SmoTie,
|
|
14
14
|
TieLine
|
|
15
15
|
} from '../data/staffModifiers';
|
|
16
|
-
import {SmoBarline, SmoMeasureModifierBase, SmoRehearsalMark,
|
|
16
|
+
import {SmoBarline, SmoMeasureModifierBase, SmoRehearsalMark, SmoTempo} from '../data/measureModifiers';
|
|
17
17
|
import {SmoPartInfo} from '../data/partInfo';
|
|
18
18
|
import {SmoMeasure} from '../data/measure';
|
|
19
19
|
import {SmoNote} from '../data/note';
|
|
@@ -164,7 +164,7 @@ export interface XmlPartGroup {
|
|
|
164
164
|
export class XmlState {
|
|
165
165
|
static get defaults() {
|
|
166
166
|
return {
|
|
167
|
-
divisions: 4096, tempo: new
|
|
167
|
+
divisions: 4096, tempo: new SmoTempo(SmoTempo.defaults), timeSignature: '4/4', keySignature: 'c',
|
|
168
168
|
clefInfo: [], staffGroups: [], smoStaves: []
|
|
169
169
|
};
|
|
170
170
|
}
|
|
@@ -195,7 +195,7 @@ export class XmlState {
|
|
|
195
195
|
tupletStatesInProgress: Record<number, XmlTupletState> = {};
|
|
196
196
|
|
|
197
197
|
tickCursor: number = 0;
|
|
198
|
-
tempo:
|
|
198
|
+
tempo: SmoTempo = new SmoTempo(SmoTempo.defaults);
|
|
199
199
|
staffArray: XmlStaffInfo[] = [];
|
|
200
200
|
staffIndex: number = 0;
|
|
201
201
|
graceNotes: SmoGraceNote[] = [];
|
package/src/smo/mxml/xmlToSmo.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { XmlHelpers } from './xmlHelpers';
|
|
|
8
8
|
import { XmlVoiceInfo, XmlState, XmlWedgeInfo } from './xmlState';
|
|
9
9
|
import { SmoLayoutManager, SmoPageLayout, SmoSystemGroup } from '../data/scoreModifiers';
|
|
10
10
|
import { SmoTextGroup } from '../data/scoreText';
|
|
11
|
-
import {
|
|
11
|
+
import { SmoTempo, SmoMeasureFormat, SmoMeasureModifierBase, SmoVolta, SmoBarline } from '../data/measureModifiers';
|
|
12
12
|
import { SmoScore, isEngravingFont } from '../data/score';
|
|
13
13
|
import { SmoMeasure, SmoMeasureParams } from '../data/measure';
|
|
14
14
|
import { SmoMusic } from '../data/music';
|
|
@@ -397,11 +397,11 @@ export class XmlToSmo {
|
|
|
397
397
|
static tempo(element: Element) {
|
|
398
398
|
let tempoText = '';
|
|
399
399
|
let customText = tempoText;
|
|
400
|
-
const rv: { staffId: number, tempo:
|
|
400
|
+
const rv: { staffId: number, tempo: SmoTempo }[] = [];
|
|
401
401
|
const soundNodes = XmlHelpers.getChildrenFromPath(element,
|
|
402
402
|
['sound']);
|
|
403
403
|
soundNodes.forEach((sound) => {
|
|
404
|
-
let tempoMode =
|
|
404
|
+
let tempoMode = SmoTempo.tempoModes.durationMode;
|
|
405
405
|
tempoText = sound.getAttribute('tempo') as string;
|
|
406
406
|
if (tempoText) {
|
|
407
407
|
const bpm = parseInt(tempoText, 10);
|
|
@@ -410,20 +410,20 @@ export class XmlToSmo {
|
|
|
410
410
|
tempoText = wordNode.length ? wordNode[0].textContent as string :
|
|
411
411
|
tempoText.toString();
|
|
412
412
|
if (isNaN(parseInt(tempoText, 10))) {
|
|
413
|
-
if (
|
|
414
|
-
tempoMode =
|
|
413
|
+
if (SmoTempo.tempoTexts[tempoText.toLowerCase()]) {
|
|
414
|
+
tempoMode = SmoTempo.tempoModes.textMode;
|
|
415
415
|
} else {
|
|
416
|
-
tempoMode =
|
|
416
|
+
tempoMode = SmoTempo.tempoModes.customMode;
|
|
417
417
|
customText = tempoText;
|
|
418
418
|
}
|
|
419
419
|
}
|
|
420
|
-
const params =
|
|
420
|
+
const params = SmoTempo.defaults;
|
|
421
421
|
params.tempoMode = tempoMode;
|
|
422
422
|
params.bpm = bpm;
|
|
423
423
|
params.tempoText = tempoText;
|
|
424
424
|
params.customText = customText;
|
|
425
425
|
params.display = true;
|
|
426
|
-
const tempo = new
|
|
426
|
+
const tempo = new SmoTempo(params);
|
|
427
427
|
const staffId = XmlHelpers.getStaffId(element);
|
|
428
428
|
rv.push({ staffId, tempo });
|
|
429
429
|
}
|
|
@@ -567,7 +567,7 @@ export class XmlToSmo {
|
|
|
567
567
|
// Only display tempo if changes.
|
|
568
568
|
if (tempo.length) {
|
|
569
569
|
// TODO: staff ID is with tempo, but tempo is per column in SMO
|
|
570
|
-
if (!
|
|
570
|
+
if (!SmoTempo.eq(xmlState.tempo, tempo[0].tempo)) {
|
|
571
571
|
xmlState.tempo = tempo[0].tempo;
|
|
572
572
|
xmlState.tempo.display = true;
|
|
573
573
|
}
|
|
@@ -13,7 +13,7 @@ import { TickMap } from './tickMap';
|
|
|
13
13
|
import { SmoSystemStaff } from '../data/systemStaff';
|
|
14
14
|
import { getId, Clef, Pitch } from '../data/common';
|
|
15
15
|
import {SmoUnmakeTupletActor} from "./tickDuration";
|
|
16
|
-
import {
|
|
16
|
+
import { SmoTempo, SmoTimeSignature } from '../data/measureModifiers';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Used to calculate the offset and transposition of a note to be pasted
|
|
@@ -212,8 +212,8 @@ export class PasteBuffer {
|
|
|
212
212
|
// Ordinarily, the key/tempo/time is mapped to the stave, but since we are pasting measure-by
|
|
213
213
|
// measure here, we want to preserve it.
|
|
214
214
|
clonedMeasure.keySignature = measureSelection.measure.keySignature;
|
|
215
|
-
clonedMeasure.timeSignature = new
|
|
216
|
-
clonedMeasure.tempo = new
|
|
215
|
+
clonedMeasure.timeSignature = new SmoTimeSignature(measureSelection.measure.timeSignature);
|
|
216
|
+
clonedMeasure.tempo = new SmoTempo(measureSelection.measure.tempo);
|
|
217
217
|
this.measures.push(clonedMeasure);
|
|
218
218
|
|
|
219
219
|
const firstMeasure = this.measures[0];
|
|
@@ -10,8 +10,8 @@ import { SmoArticulation, SmoGraceNote, SmoLyric, SmoMicrotone, SmoOrnament,
|
|
|
10
10
|
SmoDynamicText,
|
|
11
11
|
SmoTabNote} from '../data/noteModifiers';
|
|
12
12
|
import {
|
|
13
|
-
SmoRehearsalMark, SmoMeasureText, SmoVolta, SmoMeasureFormat,
|
|
14
|
-
|
|
13
|
+
SmoRehearsalMark, SmoMeasureText, SmoVolta, SmoMeasureFormat, SmoTempo, SmoBarline,
|
|
14
|
+
SmoTimeSignature, SmoRepeatSymbol
|
|
15
15
|
} from '../data/measureModifiers';
|
|
16
16
|
import { SmoStaffHairpin, SmoSlur, SmoTie, StaffModifierBase, SmoTieParams, SmoInstrument, SmoStaffHairpinParams,
|
|
17
17
|
SmoSlurParams, SmoInstrumentMeasure, SmoStaffTextBracket, SmoStaffTextBracketParams,
|
|
@@ -139,7 +139,7 @@ export class SmoOperation {
|
|
|
139
139
|
score.staves[tabStaves[0].startSelector.staff].removeTabStaves(tabStaves);
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
|
-
static setTimeSignature(score: SmoScore, selections: SmoSelection[], timeSignature:
|
|
142
|
+
static setTimeSignature(score: SmoScore, selections: SmoSelection[], timeSignature: SmoTimeSignature) {
|
|
143
143
|
const selectors: SmoSelector[] = [];
|
|
144
144
|
let i = 0;
|
|
145
145
|
// change the time signature for each stave in the score
|
|
@@ -151,7 +151,7 @@ export class SmoOperation {
|
|
|
151
151
|
});
|
|
152
152
|
selectors.forEach((selector: SmoSelector) => {
|
|
153
153
|
const rowSelection: SmoSelection = (SmoSelection.measureSelection(score, selector.staff, selector.measure) as SmoSelection);
|
|
154
|
-
rowSelection.measure.timeSignature = new
|
|
154
|
+
rowSelection.measure.timeSignature = new SmoTimeSignature(timeSignature);
|
|
155
155
|
rowSelection.measure.alignNotesWithTimeSignature();
|
|
156
156
|
});
|
|
157
157
|
}
|
|
@@ -744,7 +744,7 @@ export class SmoOperation {
|
|
|
744
744
|
});
|
|
745
745
|
}
|
|
746
746
|
|
|
747
|
-
static addTempo(score: SmoScore, selection: SmoSelection, tempo:
|
|
747
|
+
static addTempo(score: SmoScore, selection: SmoSelection, tempo: SmoTempo) {
|
|
748
748
|
score.staves.forEach((staff) => {
|
|
749
749
|
staff.addTempo(tempo, selection.selector.measure);
|
|
750
750
|
});
|
|
@@ -1050,7 +1050,10 @@ export class SmoOperation {
|
|
|
1050
1050
|
}
|
|
1051
1051
|
});
|
|
1052
1052
|
selections[0].staff.measureInstrumentMap = instMap;
|
|
1053
|
-
|
|
1053
|
+
// Don't update transpositions if everything is in concert key.
|
|
1054
|
+
if (!score.preferences.transposingScore){
|
|
1055
|
+
selections[0].staff.updateInstrumentOffsets();
|
|
1056
|
+
}
|
|
1054
1057
|
score.setNoteInstrumentProperties();
|
|
1055
1058
|
}
|
|
1056
1059
|
static computeMultipartRest(score: SmoScore) {
|
|
@@ -277,8 +277,16 @@ export class SmoStretchNoteActor extends TickIteratorBase {
|
|
|
277
277
|
stemTicksUsed: number,
|
|
278
278
|
multiplier: number
|
|
279
279
|
) {
|
|
280
|
-
const
|
|
281
|
-
|
|
280
|
+
const originalTickCount = this.notes.reduce((a, b) => a + b.tickCount, 0);
|
|
281
|
+
const measureTicks = this.measure.timeSignature.ticksFromTimeSignature() -
|
|
282
|
+
(originalTickCount + (replacingNote.tickCount - originalNote.tickCount));
|
|
283
|
+
let remainingTicks = stemTicksUsed - this.newStemTicks;
|
|
284
|
+
// If this is the last note in the measure, and the measure doesn't have the full duration of notes,
|
|
285
|
+
// allow stretching. This isn't something that can usually happen,
|
|
286
|
+
// except in cases like import from xml, time signature changes, etc.
|
|
287
|
+
if (remainingTicks < 0 && measureTicks >= 0) {
|
|
288
|
+
remainingTicks = 0;
|
|
289
|
+
}
|
|
282
290
|
if (remainingTicks >= 0) {
|
|
283
291
|
this.notesToInsert.push(replacingNote);
|
|
284
292
|
|
package/src/styles/dialogs.css
CHANGED
|
@@ -1196,6 +1196,10 @@ button.translate-submit-button {
|
|
|
1196
1196
|
#render-progress[value]::-webkit-progress-value {
|
|
1197
1197
|
background: #F70;
|
|
1198
1198
|
}
|
|
1199
|
+
body.progress-modal.printing #render-progress,
|
|
1200
|
+
body.progress-modal.printing .navbar-expand {
|
|
1201
|
+
display: none;
|
|
1202
|
+
}
|
|
1199
1203
|
body.progress-modal #render-progress {
|
|
1200
1204
|
display: block;
|
|
1201
1205
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SuiButton, SuiButtonParams } from './button';
|
|
2
2
|
import { createAndDisplayDialog } from '../dialogs/dialog';
|
|
3
3
|
import { SuiKeySignatureDialog } from '../dialogs/keySignature';
|
|
4
|
-
import {
|
|
4
|
+
import { SuiTimeSignatureDialogVue } from '../dialogs/timeSignature';
|
|
5
5
|
import { SuiTempoDialog } from '../dialogs/tempo';
|
|
6
6
|
import { SuiScoreViewDialogVue } from '../dialogs/scoreView';
|
|
7
7
|
import { KeyEvent } from '../../smo/data/common';
|
|
@@ -96,7 +96,7 @@ export class DisplaySettings extends SuiButton {
|
|
|
96
96
|
if (!this.completeNotifier) {
|
|
97
97
|
return;
|
|
98
98
|
}
|
|
99
|
-
|
|
99
|
+
SuiTimeSignatureDialogVue({
|
|
100
100
|
completeNotifier: this.completeNotifier,
|
|
101
101
|
view: this.view,
|
|
102
102
|
eventSource: this.eventSource,
|
package/src/ui/buttons/ribbon.ts
CHANGED
|
@@ -19,7 +19,7 @@ import { createApp, ref, reactive, watch } from 'vue';
|
|
|
19
19
|
import { SuiKeySignatureDialog } from '../dialogs/keySignature';
|
|
20
20
|
import { default as ribbonApp } from '../components/buttons/ribbon.vue';
|
|
21
21
|
import { default as ribbonSidebarApp } from '../components/buttons/sidebar.vue';
|
|
22
|
-
import {
|
|
22
|
+
import { SuiTimeSignatureDialogVue } from '../dialogs/timeSignature';
|
|
23
23
|
import { SuiScoreViewDialogVue } from '../dialogs/scoreView';
|
|
24
24
|
|
|
25
25
|
declare var $: any;
|
|
@@ -150,7 +150,7 @@ export class RibbonButtons {
|
|
|
150
150
|
if (!this.controller) {
|
|
151
151
|
return;
|
|
152
152
|
}
|
|
153
|
-
|
|
153
|
+
SuiTimeSignatureDialogVue({
|
|
154
154
|
completeNotifier: this.controller,
|
|
155
155
|
view: this.view,
|
|
156
156
|
eventSource: this.eventSource,
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, Ref, watch, reactive } from 'vue';
|
|
3
|
+
import numberInputApp from './numberInput.vue';
|
|
4
|
+
import {
|
|
5
|
+
TimeSignatureTime, SmoTimeSignature
|
|
6
|
+
} from '../../../smo/data/measureModifiers';
|
|
7
|
+
import dialogContainer from './dialogContainer.vue';
|
|
8
|
+
import { SelectOption } from '../../common';
|
|
9
|
+
import selectComp from './select.vue';
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
domId: string,
|
|
13
|
+
label: string,
|
|
14
|
+
timeSignature: SmoTimeSignature,
|
|
15
|
+
updateTimeSignatureCb: (mf: SmoTimeSignature) => Promise<void>,
|
|
16
|
+
updateApplyTo: (value: string) => Promise<void>,
|
|
17
|
+
commitCb: () => Promise<void>,
|
|
18
|
+
cancelCb: () => Promise<void>
|
|
19
|
+
}
|
|
20
|
+
const props = defineProps<Props>();
|
|
21
|
+
const durationSelection: SelectOption[] = [{
|
|
22
|
+
value: '2',
|
|
23
|
+
label: '2'
|
|
24
|
+
}, {
|
|
25
|
+
value: '4',
|
|
26
|
+
label: '4'
|
|
27
|
+
}, {
|
|
28
|
+
value: '8',
|
|
29
|
+
label: '8'
|
|
30
|
+
}];
|
|
31
|
+
|
|
32
|
+
const applyToOptions: SelectOption[] = [{
|
|
33
|
+
value: "Score",
|
|
34
|
+
label: 'Score'
|
|
35
|
+
}, {
|
|
36
|
+
value: "Selected",
|
|
37
|
+
label: 'Selected Measures'
|
|
38
|
+
}, {
|
|
39
|
+
value: "Remaining",
|
|
40
|
+
label: 'Remaining Measures'
|
|
41
|
+
}];
|
|
42
|
+
const applyTo: Ref<string> = ref('Selected');
|
|
43
|
+
const mainBeat = ref(4);
|
|
44
|
+
const mainDuration = ref(4);
|
|
45
|
+
const altDuration = ref(4);
|
|
46
|
+
const altDurationString = ref('4');
|
|
47
|
+
const mainDurationString = ref('4');
|
|
48
|
+
const altBeat = ref(0);
|
|
49
|
+
const isCompound = ref(false);
|
|
50
|
+
const durationFromString = (str: string): number => {
|
|
51
|
+
const val = parseInt(str);
|
|
52
|
+
if (str === '2' || str === '4' || str === '8') {
|
|
53
|
+
return val;
|
|
54
|
+
}
|
|
55
|
+
return 4;
|
|
56
|
+
}
|
|
57
|
+
watch(mainDuration, async (newVal) => {
|
|
58
|
+
if (mainDuration.value === storedTimeSignature.times[0].beatDuration) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
storedTimeSignature.times[0].beatDuration = newVal;
|
|
62
|
+
await props.updateTimeSignatureCb(storedTimeSignature);
|
|
63
|
+
});
|
|
64
|
+
watch(altDuration, async (newVal) => {
|
|
65
|
+
if (storedTimeSignature.times.length === 1) {
|
|
66
|
+
storedTimeSignature.times.push({ actualBeats: altBeat.value, beatDuration: altDuration.value });
|
|
67
|
+
}
|
|
68
|
+
else if (newVal === storedTimeSignature.times[1].beatDuration) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
storedTimeSignature.times[1].beatDuration = newVal;
|
|
72
|
+
await props.updateTimeSignatureCb(storedTimeSignature);
|
|
73
|
+
});
|
|
74
|
+
watch(altBeat, async (newVal) => {
|
|
75
|
+
if (storedTimeSignature.times.length === 1 && newVal > 0) {
|
|
76
|
+
storedTimeSignature.times.push({ actualBeats: newVal, beatDuration: storedTimeSignature.times[0].beatDuration });
|
|
77
|
+
}
|
|
78
|
+
else if (newVal < 1) {
|
|
79
|
+
if (storedTimeSignature.times.length > 1) {
|
|
80
|
+
storedTimeSignature.times.pop();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else if (storedTimeSignature.times.length > 1 && newVal === storedTimeSignature.times[1].actualBeats) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
storedTimeSignature.times[1].actualBeats = newVal;
|
|
87
|
+
await props.updateTimeSignatureCb(storedTimeSignature);
|
|
88
|
+
});
|
|
89
|
+
watch(mainDurationString, (newVal) => {
|
|
90
|
+
mainDuration.value = durationFromString(newVal);
|
|
91
|
+
});
|
|
92
|
+
watch(altDurationString, (newVal) => {
|
|
93
|
+
altDuration.value = durationFromString(newVal);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const { domId, label, commitCb, cancelCb, } = { ...props };
|
|
97
|
+
const useSymbol: Ref<boolean> = ref(props.timeSignature.useSymbol);
|
|
98
|
+
const display: Ref<boolean> = ref(props.timeSignature.display);
|
|
99
|
+
const displayString: Ref<string> = ref(props.timeSignature.displayString);
|
|
100
|
+
const storedTimeSignature = new SmoTimeSignature(props.timeSignature);
|
|
101
|
+
if (storedTimeSignature.times.length > 1) {
|
|
102
|
+
isCompound.value = true;
|
|
103
|
+
altBeat.value = storedTimeSignature.times[1].actualBeats;
|
|
104
|
+
altDuration.value = storedTimeSignature.times[1].beatDuration;
|
|
105
|
+
}
|
|
106
|
+
mainBeat.value = storedTimeSignature.times[0].actualBeats;
|
|
107
|
+
mainDuration.value = storedTimeSignature.times[0].beatDuration;
|
|
108
|
+
const updateMainBeat = async (newVal: number) => {
|
|
109
|
+
mainBeat.value = newVal;
|
|
110
|
+
storedTimeSignature.times[0].actualBeats = newVal;
|
|
111
|
+
await props.updateTimeSignatureCb(storedTimeSignature);
|
|
112
|
+
}
|
|
113
|
+
const updateMainDurationString = async (newVal: string) => {
|
|
114
|
+
mainDurationString.value = newVal;
|
|
115
|
+
storedTimeSignature.times[0].beatDuration = durationFromString(newVal);
|
|
116
|
+
await props.updateTimeSignatureCb(storedTimeSignature);
|
|
117
|
+
}
|
|
118
|
+
const updateAltDurationString = async (newVal: string) => {
|
|
119
|
+
altDurationString.value = newVal;
|
|
120
|
+
storedTimeSignature.times[1].beatDuration = durationFromString(newVal);
|
|
121
|
+
await props.updateTimeSignatureCb(storedTimeSignature);
|
|
122
|
+
}
|
|
123
|
+
const updateAltBeat = async (newVal: number) => {
|
|
124
|
+
if (storedTimeSignature.times.length > 1 && storedTimeSignature.times[1].actualBeats === newVal) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (newVal <= 1) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (storedTimeSignature.times.length === 1) {
|
|
131
|
+
storedTimeSignature.times.push({ actualBeats: newVal, beatDuration: storedTimeSignature.times[0].beatDuration });
|
|
132
|
+
} else {
|
|
133
|
+
storedTimeSignature.times[1].actualBeats = newVal;
|
|
134
|
+
}
|
|
135
|
+
await props.updateTimeSignatureCb(storedTimeSignature);
|
|
136
|
+
}
|
|
137
|
+
watch(isCompound, async (newVal: boolean, oldVal: boolean) => {
|
|
138
|
+
if (newVal === oldVal) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (newVal) {
|
|
142
|
+
altBeat.value = 4;
|
|
143
|
+
} else {
|
|
144
|
+
altBeat.value = 0;
|
|
145
|
+
}
|
|
146
|
+
await props.updateTimeSignatureCb(storedTimeSignature);
|
|
147
|
+
});
|
|
148
|
+
const getId = (str: string) => {
|
|
149
|
+
return `${props.domId}-${str}`;
|
|
150
|
+
}
|
|
151
|
+
</script>
|
|
152
|
+
|
|
153
|
+
<template>
|
|
154
|
+
<dialogContainer :domId="domId" :label="label" :cancelCb="cancelCb" :commitCb="commitCb"
|
|
155
|
+
:classes="'text-center mw-40 nw-40'">
|
|
156
|
+
<div class="row align-items-center">
|
|
157
|
+
<div class="checkbox-input-column-div">
|
|
158
|
+
<input class="form-check-input" type="checkbox" v-model="useSymbol" :id="getId('use-symbol')">
|
|
159
|
+
</input>
|
|
160
|
+
</div>
|
|
161
|
+
<div class="checkbox-input-label-div">
|
|
162
|
+
<span class="form-check-label" :for="getId('use-symbol')">Use Symbol</span>
|
|
163
|
+
</div>
|
|
164
|
+
<div class="checkbox-input-column-div">
|
|
165
|
+
<input class="form-check-input" type="checkbox" v-model="display" :id="getId('display-ts')">
|
|
166
|
+
</input>
|
|
167
|
+
</div>
|
|
168
|
+
<div class="checkbox-input-label-div">
|
|
169
|
+
<span class="form-check-label" :for="getId('display-cs')">Display Time Signature</span>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
<div class="row align-items-center">
|
|
173
|
+
<div class="checkbox-input-column-div">
|
|
174
|
+
<input class="form-check-input" type="checkbox" v-model="isCompound" :id="getId('is-compound')">
|
|
175
|
+
</input>
|
|
176
|
+
</div>
|
|
177
|
+
<div class="checkbox-input-label-div">
|
|
178
|
+
<span class="form-check-label" :for="getId('system-break')">Compound Time</span>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
<div class="row align-items-center">
|
|
182
|
+
<div class="col col-4">
|
|
183
|
+
<numberInputApp :domId="getId('num-main')" :initialValue="mainBeat" :changeCb="updateMainBeat" :precision="0"
|
|
184
|
+
:width="25" :minValue="1" :maxValue="16" />
|
|
185
|
+
</div>
|
|
186
|
+
<div class="number-input-label-div col col-4">
|
|
187
|
+
<span class="form-check-label">Beats/Measure</span>
|
|
188
|
+
</div>
|
|
189
|
+
<div class="col col-6">
|
|
190
|
+
<selectComp :domId="getId('main-duration')" :label="'Beat Duration'" :selections="durationSelection"
|
|
191
|
+
:initialValue="mainDurationString" :changeCb="updateMainDurationString" />
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
<div class="row align-items-center" :class="{ hide: !isCompound }">
|
|
195
|
+
<div class="col col-4">
|
|
196
|
+
<numberInputApp :domId="getId('num-alt')" :initialValue="altBeat" :changeCb="updateAltBeat" :precision="0"
|
|
197
|
+
:width="25" :minValue="0" :maxValue="16" />
|
|
198
|
+
</div>
|
|
199
|
+
<div class="number-input-label-div col col-4">
|
|
200
|
+
<span class="form-check-label">Compound Beats/Measure</span>
|
|
201
|
+
</div>
|
|
202
|
+
<div class="col col-6">
|
|
203
|
+
<selectComp :domId="getId('alt-duration')" :label="'Beat Duration'" :selections="durationSelection"
|
|
204
|
+
:initialValue="altDurationString" :changeCb="updateAltDurationString" />
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
<div class="row align-items-center">
|
|
208
|
+
<div class="col col-4">
|
|
209
|
+
<input type="text" class="form-control form-control-sm" v-model="displayString" :id="getId('display-string')" />
|
|
210
|
+
</div>
|
|
211
|
+
<div class="number-input-label-div col col-8">
|
|
212
|
+
<span class="form-check-lable">Alternate Display String (for pickups)</span>
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
<div class="row mb-2">
|
|
216
|
+
<div class="col col-3 text-end">Apply To</div>
|
|
217
|
+
<div class="col col-6">
|
|
218
|
+
<selectComp :domId="getId('page-size-select')" :label="''" :selections="applyToOptions" :initialValue="applyTo"
|
|
219
|
+
:changeCb="props.updateApplyTo" />
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
</dialogContainer>
|
|
223
|
+
</template>
|
|
@@ -65,7 +65,7 @@ export const SuiFileUploadDialog = async (parameters: SuiDialogParams) => {
|
|
|
65
65
|
if (filename.endsWith('.mid') || filename.endsWith('.midi')) {
|
|
66
66
|
await commitMidi();
|
|
67
67
|
}
|
|
68
|
-
else if (filename.endsWith('.xml') || filename.endsWith('.mxml')) {
|
|
68
|
+
else if (filename.endsWith('.xml') || filename.endsWith('.mxml') || filename.endsWith('.mxl')) {
|
|
69
69
|
await commitXml();
|
|
70
70
|
}
|
|
71
71
|
else if (filename.endsWith('.json')) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// [Smoosic](https://github.com/AaronDavidNewman/Smoosic)
|
|
2
2
|
// Copyright (c) Aaron David Newman 2021.
|
|
3
|
-
import {
|
|
3
|
+
import { SmoTempo, SmoTempoNumberAttribute, SmoTempoStringAttribute, SmoTempoBooleanAttribute } from '../../smo/data/measureModifiers';
|
|
4
4
|
import { SmoSelection, SmoSelector } from '../../smo/xform/selections';
|
|
5
5
|
import { SuiScoreViewOperations } from '../../render/sui/scoreViewOperations';
|
|
6
6
|
import { DialogDefinition, SuiDialogParams } from './dialog';
|