smoosic 1.0.12 → 1.0.15
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 +1 -0
- package/build/smoosic.js +34 -23
- package/package.json +1 -1
- package/release/smoosic.js +34 -23
- package/src/application/configuration.ts +1 -1
- package/src/application/exports.ts +4 -3
- package/src/common/vex.ts +11 -0
- package/src/render/audio/musicCursor.ts +3 -1
- package/src/render/audio/samples.ts +2 -2
- package/src/render/sui/mapper.ts +10 -4
- package/src/render/sui/scoreRender.ts +5 -1
- package/src/render/sui/scoreViewOperations.ts +26 -0
- package/src/render/vex/vxMeasure.ts +1 -0
- package/src/smo/data/measure.ts +1 -0
- package/src/smo/data/noteModifiers.ts +85 -1
- package/src/smo/data/score.ts +10 -0
- package/src/smo/data/scoreModifiers.ts +11 -3
- package/src/smo/data/staffModifiers.ts +14 -5
- package/src/smo/data/systemStaff.ts +11 -0
- package/src/smo/xform/copypaste.ts +50 -1
- package/src/smo/xform/operations.ts +15 -0
- package/src/ui/dialogs/endings.ts +1 -1
- package/src/ui/dialogs/instrument.ts +11 -1
- package/src/ui/dialogs/preferences.ts +11 -0
- package/src/ui/keyBindings/default/trackerKeys.ts +1 -1
- package/src/ui/menus/edit.ts +79 -0
- package/src/ui/menus/manager.ts +3 -0
- package/src/ui/ribbonLayout/default/defaultRibbon.ts +12 -2
- package/release/library/BachCantataBMV68.json +0 -1
- package/release/library/BachCantataBMV682.json +0 -1
- package/release/library/Beethoven_AnDieFerneGeliebte.xml +0 -4271
- package/release/library/Dichterliebe01.xml +0 -7613
- package/release/library/MozartTrio.xml +0 -3498
- package/release/library/Mozart_AnChloe.xml +0 -3986
- package/release/library/Postillionlied.json +0 -1
- package/release/library/ScottJoplin_The_Entertainer.xml +0 -20741
- package/release/library/Yama2.json +0 -1
- package/release/library/bigband/Bilongo-Mandinga.json +0 -1
- package/release/library/bigband/Plena.json +0 -1
- package/release/library/bigband/Poinciana.json +0 -1
- package/release/library/bigband/child who drums.json +0 -1
- package/release/library/cp/Another Hairdo.mxl +0 -0
- package/release/library/cp/Anthropology Eb.mxl +0 -0
- package/release/library/cp/Au Privave No 1 Eb.mxl +0 -0
- package/release/library/cp/Au Privave No 2 Eb.mxl +0 -0
- package/release/library/cp/Back Home Blues.mxl +0 -0
- package/release/library/cp/Ballade.mxl +0 -0
- package/release/library/cp/Billies Bounce.mxl +0 -0
- package/release/library/cp/Bloomdido.mxl +0 -0
- package/release/library/cp/Blues for Alice.mxl +0 -0
- package/release/library/cp/Celerity Alto.mxl +0 -0
- package/release/library/cp/Celerity.mxl +0 -0
- package/release/library/cp/Cheryl F07.mxl +0 -0
- package/release/library/cp/Cheryl.mxl +0 -0
- package/release/library/cp/Confirmation Eb 09.mxl +0 -0
- package/release/library/cp/Cool Blues Alto1.mxl +0 -0
- package/release/library/cp/Dewey Square.mxl +0 -0
- package/release/library/cp/Donna Lee Eb.musicxml +0 -9134
- package/release/library/cp/Donna Lee Eb.mxl +0 -0
- package/release/library/cp/Ko Ko Eb.mxl +0 -0
- package/release/library/cp/Little Benny.mxl +0 -0
- package/release/library/cp/Moose the Mooche Eb.mxl +0 -0
- package/release/library/cp/Night in Tunisia solo.mxl +0 -0
- package/release/library/cp/Nows the Time No 1.mxl +0 -0
- package/release/library/cp/Nows the Time No 2.mxl +0 -0
- package/release/library/cp/Ornithology Eb.mxl +0 -0
- package/release/library/cp/Parkers Mood.mxl +0 -0
- package/release/library/cp/Passport.mxl +0 -0
- package/release/library/cp/Red Cross.mxl +0 -0
- package/release/library/cp/Scrapple from the Apple Eb.mxl +0 -0
- package/release/library/cp/Shaw Nuff Eb.mxl +0 -0
- package/release/library/cp/Shaw Nuff.mxl +0 -0
- package/release/library/cp/She Rote No1.mxl +0 -0
- package/release/library/cp/She Rote No2.mxl +0 -0
- package/release/library/cp/The Bird 07.mxl +0 -0
- package/release/library/cp/The Bird.mxl +0 -0
- package/release/library/cp/Warming Up A Riff.mxl +0 -0
- package/release/library/cp/Yardbird Suite (alto solo).mxl +0 -0
- package/release/library/cp/Yardbird Suite Eb Alto+Bass+Chords.mxl +0 -0
- package/release/library/cp/Yardbird Suite Eb.mxl +0 -0
- package/release/library/cp/cplist.txt +0 -40
- package/release/library/hymns/AdesteFideles.json +0 -1
- package/release/library/hymns/Heshallfeed.json +0 -1
- package/release/library/hymns/Hyfrydol.json +0 -1
- package/release/library/hymns/Lakeshore.json +0 -1
- package/release/library/hymns/Precious Lord.json +0 -1
- package/release/library/hymns/Shade.json +0 -1
- package/release/library/hymns/SpiritofLivingGod.json +0 -1
- package/release/library/hymns/SweetSpirit.json +0 -1
- package/release/library/hymns/We3kings.json +0 -1
- package/release/library/hymns/Weisse-Flaggen.json +0 -1
- package/release/library/hymns/WorldPeacePrayer.json +0 -1
- package/release/library/hymns/balm.json +0 -1
- package/release/library/hymns/cplist.txt +0 -9
- package/release/library/hymns/nearerMyGod.json +0 -1
- package/release/library/links/bigband.json +0 -47
- package/release/library/links/cptranscriptions.json +0 -365
- package/release/library/links/hymns.json +0 -78
- package/release/library/links/messiah.json +0 -33
- package/release/library/links/musicxml.json +0 -62
- package/release/library/links/piano.json +0 -59
- package/release/library/links/samples.json +0 -75
- package/release/library/links/smoLibrary.json +0 -68
- package/release/library/links/soprano.json +0 -51
- package/release/library/links/transcriptions.json +0 -32
- package/release/library/links/trumpet.json +0 -76
- package/release/library/messiah/Messiah-I-1.json +0 -1
- package/release/library/messiah/Messiah-I-2.json +0 -1
- package/release/library/miditest/keytime.mid +0 -0
- package/release/library/miditest/ties.mid +0 -0
- package/release/library/miditest/triplet.mid +0 -0
- package/release/library/piano/20seconds.json +0 -1
- package/release/library/piano/BachInvention.json +0 -1
- package/release/library/piano/BachWTC-3.json +0 -1
- package/release/library/piano/Gnossienne3-3.json +0 -1
- package/release/library/piano/Gymnopedie No 1 (8).json +0 -1
- package/release/library/piano/Joplin-Entertainer.json +0 -1
- package/release/library/soprano/Gesu Bambino.json +0 -1
- package/release/library/soprano/LaudamusTrumpetSoprano.json +0 -1
- package/release/library/soprano/Postillionlied.json +0 -1
- package/release/library/soprano/Solovey.json +0 -1
- package/release/library/soprano/ThereIsABalm.json +0 -1
- package/release/library/trumpet/2min.json +0 -1
- package/release/library/trumpet/BachAirOrganTpt.json +0 -1
- package/release/library/trumpet/BachCantataBMV68.json +0 -1
- package/release/library/trumpet/BeethovenOp21-old.json +0 -1
- package/release/library/trumpet/BeethovenOp21.json +0 -1
- package/release/library/trumpet/Fireworks - trumpets.json +0 -1
- package/release/library/trumpet/HymnFreedom.json +0 -1
- package/release/library/trumpet/TelemannSonata3rd.json +0 -1
- package/release/library/xml/Gnossienne_No._3.mxl +0 -0
- package/release/library/xml/M_Book_1_Part_1_The_Open_Well_Tempered_Clavier.mxl +0 -0
- package/release/library/xml/lg-177775783.xml +0 -8674
|
@@ -86,7 +86,7 @@ export interface SmoConfigurationParams {
|
|
|
86
86
|
leftControls: 'controls-left',
|
|
87
87
|
topControls: 'controls-top',
|
|
88
88
|
scoreDomContainer: 'smo-scroll-region',
|
|
89
|
-
libraryUrl: 'https://smoosic.github.io/
|
|
89
|
+
libraryUrl: 'https://smoosic.github.io/SmoScores/links/smoLibrary.json',
|
|
90
90
|
demonPollTime: 50, // how often we poll the score to see if it changed
|
|
91
91
|
idleRedrawTime: 1000, // maximum time between score modification and render
|
|
92
92
|
ribbonLayout: defaultRibbonLayout.ribbons,
|
|
@@ -40,7 +40,7 @@ import { SuiInstrumentDialog } from '../ui/dialogs/instrument';
|
|
|
40
40
|
import { SuiTimeSignatureDialog } from '../ui/dialogs/timeSignature';
|
|
41
41
|
import { SuiTempoDialog } from '../ui/dialogs/tempo';
|
|
42
42
|
import { SuiNoteHeadAdapter, SuiNoteHeadDialog, SuiNoteHeadButtonComponent, SuiStemButtonComponent } from '../ui/dialogs/noteHead';
|
|
43
|
-
import { SuiEndingsAdapter, SuiEndingsDialog,
|
|
43
|
+
import { SuiEndingsAdapter, SuiEndingsDialog, endingsButtonFactory,
|
|
44
44
|
SuiEndBarButtonComponent, SuiStartBarButtonComponent, SuiRepeatSymbolButtonComponent } from '../ui/dialogs/endings';
|
|
45
45
|
import { SuiScoreIdentificationDialog } from '../ui/dialogs/scoreId';
|
|
46
46
|
import { SuiScorePreferencesDialog } from '../ui/dialogs/preferences';
|
|
@@ -98,6 +98,7 @@ import { TextCheckComponent } from '../ui/dialogs/components/textCheck';
|
|
|
98
98
|
import { SuiMenuManager} from '../ui/menus/manager';
|
|
99
99
|
import { SuiMenuBase, SuiMenuCustomizer } from '../ui/menus/menu';
|
|
100
100
|
import { SuiScoreMenu } from '../ui/menus/score';
|
|
101
|
+
import { SuiEditMenu } from '../ui/menus/edit';
|
|
101
102
|
import { SuiTextMenu } from '../ui/menus/text';
|
|
102
103
|
import { SuiPartMenu } from '../ui/menus/parts';
|
|
103
104
|
import { SuiVoiceMenu } from '../ui/menus/voices';
|
|
@@ -346,7 +347,7 @@ export const Smo = {
|
|
|
346
347
|
// Menus
|
|
347
348
|
SuiMenuManager, SuiMenuBase, SuiMenuCustomizer, SuiScoreMenu, SuiFileMenu,
|
|
348
349
|
SuiDynamicsMenu, SuiTimeSignatureMenu, SuiKeySignatureMenu, SuiStaffModifierMenu,
|
|
349
|
-
SuiLanguageMenu, SuiMeasureMenu, SuiNoteMenu, SmoLanguage, SmoTranslator, SuiPartMenu,
|
|
350
|
+
SuiLanguageMenu, SuiMeasureMenu, SuiNoteMenu, SuiEditMenu, SmoLanguage, SmoTranslator, SuiPartMenu,
|
|
350
351
|
SuiPartSelectionMenu, SuiTextMenu, SuiVoiceMenu, SuiBeamMenu,
|
|
351
352
|
// Dialogs
|
|
352
353
|
SuiGraceNoteAdapter, SuiGraceNoteDialog, SuiGraceNoteButtonsComponent,
|
|
@@ -365,7 +366,7 @@ export const Smo = {
|
|
|
365
366
|
SuiArticulationDialog, SuiArticulationButtonComponent, SuiArticulationAdapter,
|
|
366
367
|
SuiMicrotoneAdapter, SuiMicrotoneButtonComponent, SuiMicrotoneDialog,
|
|
367
368
|
SuiNoteHeadAdapter, SuiNoteHeadDialog, SuiStemButtonComponent, SuiNoteHeadButtonComponent,
|
|
368
|
-
SuiEndingsAdapter, SuiEndingsDialog,
|
|
369
|
+
SuiEndingsAdapter, SuiEndingsDialog, endingsButtonFactory,
|
|
369
370
|
SuiEndBarButtonComponent, SuiStartBarButtonComponent, SuiRepeatSymbolButtonComponent,
|
|
370
371
|
SuiPrintFileDialog, SuiSaveFileDialog, SuiSaveXmlDialog, SuiSaveVexDialog,
|
|
371
372
|
SuiSaveMidiDialog, SuiDialogBase, createAndDisplayDialog,
|
package/src/common/vex.ts
CHANGED
|
@@ -86,6 +86,8 @@ export type TabNotePosition = VexTabNotePosition;
|
|
|
86
86
|
// @internal
|
|
87
87
|
export type TabNoteStruct = VexTabNoteStruct;
|
|
88
88
|
|
|
89
|
+
const lineDefaults = [4, 2, 0, 1, 3]; // lines to turn off if there are less than 5
|
|
90
|
+
|
|
89
91
|
/**
|
|
90
92
|
* @internal
|
|
91
93
|
*/
|
|
@@ -163,6 +165,7 @@ export interface SmoVexStaveParams {
|
|
|
163
165
|
canceledKey: string | null,
|
|
164
166
|
startX: number,
|
|
165
167
|
adjX: number,
|
|
168
|
+
lines: number,
|
|
166
169
|
context: any
|
|
167
170
|
}
|
|
168
171
|
export function createTabStave(box: SvgBox, spacing: number, numLines: number): TabStave {
|
|
@@ -224,6 +227,14 @@ export function createStave(params: SmoVexStaveParams) {
|
|
|
224
227
|
fill: 'none', 'stroke-width': 1, stroke: 'white'
|
|
225
228
|
});
|
|
226
229
|
}
|
|
230
|
+
if (params.lines < 5) {
|
|
231
|
+
const linesAr = [];
|
|
232
|
+
for (let i = 0; i < lineDefaults.length; ++i) {
|
|
233
|
+
const visible = lineDefaults[i] < params.lines;
|
|
234
|
+
linesAr.push({ visible });
|
|
235
|
+
}
|
|
236
|
+
stave.setConfigForLines(linesAr);
|
|
237
|
+
}
|
|
227
238
|
// stave.options.spaceAboveStaffLn = 0; // don't let vex place the staff, we want to.
|
|
228
239
|
stave.options.space_above_staff_ln = 0; // don't let vex place the staff, we want to.
|
|
229
240
|
// Add a clef and time signature.
|
|
@@ -81,6 +81,8 @@ export const defaultClearAudioAnimationHandler = (delay: number) => {
|
|
|
81
81
|
ctx.closeGroup();
|
|
82
82
|
ctx.restore();
|
|
83
83
|
layoutDebug.updatePlayDebug(selector, measure.svg.logicalBox);
|
|
84
|
-
|
|
84
|
+
if (view.score.preferences.autoScrollPlayback) {
|
|
85
|
+
scroller.scrollVisibleBox(zmeasureSel.measure.svg.logicalBox);
|
|
86
|
+
}
|
|
85
87
|
}
|
|
86
88
|
}
|
|
@@ -382,14 +382,14 @@ export class SuiSampleMedia {
|
|
|
382
382
|
sample: 'sample-asax-a3',
|
|
383
383
|
family: 'wind',
|
|
384
384
|
instrument: 'tenorSax',
|
|
385
|
-
nativeFrequency: SmoAudioPitch.smoPitchToFrequency({ letter: 'a', accidental: 'n', octave: 3 },
|
|
385
|
+
nativeFrequency: SmoAudioPitch.smoPitchToFrequency({ letter: 'a', accidental: 'n', octave: 3 }, 12, null),
|
|
386
386
|
});
|
|
387
387
|
SuiSampleMedia.insertIntoMap({
|
|
388
388
|
sustain: 'sustained',
|
|
389
389
|
sample: 'sample-asax-c4',
|
|
390
390
|
family: 'wind',
|
|
391
391
|
instrument: 'tenorSax',
|
|
392
|
-
nativeFrequency: SmoAudioPitch.smoPitchToFrequency({ letter: 'c', accidental: 'n', octave: 4 },
|
|
392
|
+
nativeFrequency: SmoAudioPitch.smoPitchToFrequency({ letter: 'c', accidental: 'n', octave: 4 }, 12, null),
|
|
393
393
|
});
|
|
394
394
|
SuiSampleMedia.insertIntoMap({
|
|
395
395
|
sustain: 'sustained',
|
package/src/render/sui/mapper.ts
CHANGED
|
@@ -395,8 +395,10 @@ export abstract class SuiMapper {
|
|
|
395
395
|
}
|
|
396
396
|
return true;
|
|
397
397
|
}
|
|
398
|
-
|
|
399
|
-
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* This is the logic that stores the screen location of music after it's rendered
|
|
401
|
+
*/
|
|
400
402
|
mapMeasure(staff: SmoSystemStaff, measure: SmoMeasure, printing: boolean) {
|
|
401
403
|
let voiceIx = 0;
|
|
402
404
|
let selectedTicks = 0;
|
|
@@ -433,8 +435,12 @@ export abstract class SuiMapper {
|
|
|
433
435
|
tick,
|
|
434
436
|
pitches: []
|
|
435
437
|
};
|
|
436
|
-
if (
|
|
437
|
-
|
|
438
|
+
if (measure.repeatSymbol) {
|
|
439
|
+
// Some measures have a symbol that replaces the notes. This allows us to select
|
|
440
|
+
// the measure
|
|
441
|
+
const x = measure.svg.logicalBox.x + (measure.svg.logicalBox.width / voice.notes.length) * tick;
|
|
442
|
+
const width = measure.svg.logicalBox.width / voice.notes.length;
|
|
443
|
+
note.logicalBox = { x, y: measure.svg.logicalBox.y, width, height: measure.svg.logicalBox.height };
|
|
438
444
|
}
|
|
439
445
|
// create a selection for the newly rendered note
|
|
440
446
|
const selection = new SmoSelection({
|
|
@@ -107,7 +107,11 @@ export class SuiScoreRender {
|
|
|
107
107
|
// If this text is attached to the measure, base the block location on the rendered measure location.
|
|
108
108
|
if (newGroup.attachToSelector) {
|
|
109
109
|
// If this text is attached to a staff that is not visible, don't draw it.
|
|
110
|
-
|
|
110
|
+
let mappedStaff = this.score!.staves.find((staff) => staff.staffId === newGroup.selector!.staff);
|
|
111
|
+
if (this.score?.isPartExposed() && this.score.staves[0].partInfo.preserveTextGroups) {
|
|
112
|
+
mappedStaff = this.score!.staves.find((staff) =>
|
|
113
|
+
staff.getMappedStaffId() === newGroup.selector!.staff);
|
|
114
|
+
}
|
|
111
115
|
if (!mappedStaff) {
|
|
112
116
|
return;
|
|
113
117
|
}
|
|
@@ -1235,6 +1235,32 @@ export class SuiScoreViewOperations extends SuiScoreView {
|
|
|
1235
1235
|
this.replaceMeasureView(measureRange);
|
|
1236
1236
|
await this.renderer.updatePromise();
|
|
1237
1237
|
}
|
|
1238
|
+
/**
|
|
1239
|
+
* Paste only the chords.
|
|
1240
|
+
*/
|
|
1241
|
+
async pasteChords(): Promise<void> {
|
|
1242
|
+
// We undo the whole score on a paste, since we don't yet know the
|
|
1243
|
+
// extent of the overlap
|
|
1244
|
+
this.renderer.preserveScroll();
|
|
1245
|
+
const selections: SmoSelection[] = this.getPasteMeasureList();
|
|
1246
|
+
const firstSelection = selections[0];
|
|
1247
|
+
const measureEnd = selections[selections.length - 1].selector.measure;
|
|
1248
|
+
const measureRange = [firstSelection.selector.measure, measureEnd];
|
|
1249
|
+
this.storeUndo.grouping = true;
|
|
1250
|
+
// Undo the paste by selecting all the affected measures
|
|
1251
|
+
for (let i = measureRange[0]; i <= measureRange[1]; ++i) {
|
|
1252
|
+
this._undoColumn('paste', i);
|
|
1253
|
+
this.renderer.unrenderColumn(this.score.staves[0].measures[i]);
|
|
1254
|
+
}
|
|
1255
|
+
this.storeUndo.grouping = false;
|
|
1256
|
+
const altSelection = this._getEquivalentSelection(firstSelection);
|
|
1257
|
+
const altTarget = altSelection!.selector;
|
|
1258
|
+
altTarget.tick = this.tracker.selections[0].selector.tick;
|
|
1259
|
+
this.storePaste.pasteChords(altTarget);
|
|
1260
|
+
// Refresh those measures.
|
|
1261
|
+
this.replaceMeasureView(measureRange);
|
|
1262
|
+
await this.renderer.updatePromise();
|
|
1263
|
+
}
|
|
1238
1264
|
/**
|
|
1239
1265
|
* specify a note head other than the default for the duration
|
|
1240
1266
|
* @param head
|
|
@@ -505,6 +505,7 @@ export class VxMeasure implements VxMeasureIf {
|
|
|
505
505
|
canceledKey,
|
|
506
506
|
startX: this.smoMeasure.svg.maxColumnStartX,
|
|
507
507
|
adjX: this.smoMeasure.svg.adjX,
|
|
508
|
+
lines: this.smoMeasure.lines,
|
|
508
509
|
context: this.context.getContext()
|
|
509
510
|
}
|
|
510
511
|
this.stave = createStave(smoVexStaveParams);
|
package/src/smo/data/measure.ts
CHANGED
|
@@ -861,6 +861,7 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
|
|
|
861
861
|
// The measure expects to get concert KS in constructor and adjust for instrument. So do the
|
|
862
862
|
// opposite.
|
|
863
863
|
obj.keySignature = SmoMusic.vexKeySigWithOffset(obj.keySignature, -1 * obj.transposeIndex);
|
|
864
|
+
obj.lines = params.lines;
|
|
864
865
|
// Don't redisplay tempo for a new measure
|
|
865
866
|
const rv = new SmoMeasure(obj);
|
|
866
867
|
if (rv.tempo && rv.tempo.display) {
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* @module /smo/data/noteModifiers
|
|
7
7
|
*/
|
|
8
8
|
import { SmoAttrs, Ticks, Pitch, getId, SmoObjectParams, Transposable, SvgBox, SmoModifierBase,
|
|
9
|
-
Clef, IsClef, SmoDynamicCtor
|
|
9
|
+
Clef, IsClef, SmoDynamicCtor,
|
|
10
|
+
IsPitchLetter} from './common';
|
|
10
11
|
import { smoSerialize } from '../../common/serializationHelpers';
|
|
11
12
|
import { SmoMusic } from './music';
|
|
12
13
|
import { defaultNoteScale, FontInfo, getChordSymbolGlyphFromCode } from '../../common/vex';
|
|
@@ -931,6 +932,46 @@ export class SmoLyric extends SmoNoteModifierBase {
|
|
|
931
932
|
this.adjustNoteWidthChord = val;
|
|
932
933
|
}
|
|
933
934
|
}
|
|
935
|
+
static transposeChordToKey(chord: SmoLyric, offset: number, srcKey: string, destKey: string): SmoLyric {
|
|
936
|
+
if (chord.parser !== SmoLyric.parsers.chord || offset === 0) {
|
|
937
|
+
return new SmoLyric(chord);
|
|
938
|
+
}
|
|
939
|
+
const nchord = new SmoLyric(chord);
|
|
940
|
+
let srcIx = 0;
|
|
941
|
+
const maxLen = chord.text.length - 1;
|
|
942
|
+
let destString = '';
|
|
943
|
+
while (srcIx < chord.text.length) {
|
|
944
|
+
let symbolBlock = false;
|
|
945
|
+
const nchar = chord.text[srcIx];
|
|
946
|
+
let lk = srcIx < maxLen ? chord.text[srcIx + 1] : null;
|
|
947
|
+
// make sure this chord start witha VEX pitch letter (A-G upper case)
|
|
948
|
+
if (IsPitchLetter(nchar.toLowerCase()) && nchar == nchar.toUpperCase()) {
|
|
949
|
+
if (lk === '@') {
|
|
950
|
+
symbolBlock = true;
|
|
951
|
+
srcIx += 1;
|
|
952
|
+
lk = srcIx < maxLen ? chord.text[srcIx + 1] : null;
|
|
953
|
+
}
|
|
954
|
+
const pitch: Pitch = {letter: nchar.toLowerCase() as any, accidental: 'n', octave: 4 };
|
|
955
|
+
if (lk !== null && (lk === 'b' || lk === '#')) {
|
|
956
|
+
pitch.accidental = lk;
|
|
957
|
+
srcIx += 1;
|
|
958
|
+
}
|
|
959
|
+
const npitch = SmoMusic.transposePitchForKey(pitch, srcKey, destKey, offset);
|
|
960
|
+
destString += npitch.letter.toUpperCase();
|
|
961
|
+
if (symbolBlock) {
|
|
962
|
+
destString += '@';
|
|
963
|
+
}
|
|
964
|
+
if (npitch.accidental !== 'n') {
|
|
965
|
+
destString += npitch.accidental;
|
|
966
|
+
}
|
|
967
|
+
} else {
|
|
968
|
+
destString += nchar;
|
|
969
|
+
}
|
|
970
|
+
srcIx += 1;
|
|
971
|
+
}
|
|
972
|
+
nchord.text = destString;
|
|
973
|
+
return nchord;
|
|
974
|
+
}
|
|
934
975
|
|
|
935
976
|
// ### getClassSelector
|
|
936
977
|
// returns a selector used to find this text block within a note.
|
|
@@ -1022,7 +1063,49 @@ export class SmoLyric extends SmoNoteModifierBase {
|
|
|
1022
1063
|
}
|
|
1023
1064
|
}
|
|
1024
1065
|
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Used to create a {@link SmoBarline}
|
|
1068
|
+
* @category SmoObject
|
|
1069
|
+
*/
|
|
1070
|
+
|
|
1071
|
+
export interface SmoNoteBarParams {
|
|
1072
|
+
barline: number
|
|
1073
|
+
}
|
|
1025
1074
|
|
|
1075
|
+
export interface SmoNoteBarParamsSer extends SmoNoteBarParams {
|
|
1076
|
+
ctor: string,
|
|
1077
|
+
barline: number
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
export class SmoNoteBar extends SmoNoteModifierBase {
|
|
1081
|
+
barline: number = SmoNoteBar.defaults.barline;
|
|
1082
|
+
static get defaults(): SmoNoteBarParams {
|
|
1083
|
+
return { barline: SmoNoteBar.barlines.noBar }
|
|
1084
|
+
}
|
|
1085
|
+
static get parameterArray() {
|
|
1086
|
+
return ['barline'];
|
|
1087
|
+
}
|
|
1088
|
+
static readonly barlines: Record<string, number> = {
|
|
1089
|
+
singleBar: 0,
|
|
1090
|
+
doubleBar: 1,
|
|
1091
|
+
endBar: 2,
|
|
1092
|
+
startRepeat: 3,
|
|
1093
|
+
endRepeat: 4,
|
|
1094
|
+
noBar: 5
|
|
1095
|
+
}
|
|
1096
|
+
constructor(parameters: SmoNoteBarParams) {
|
|
1097
|
+
super('SmoNoteBar');
|
|
1098
|
+
smoSerialize.serializedMerge(SmoNoteBar.parameterArray, SmoNoteBar.defaults, this);
|
|
1099
|
+
smoSerialize.serializedMerge(SmoLyric.parameterArray, parameters, this);
|
|
1100
|
+
}
|
|
1101
|
+
serialize(): SmoNoteBarParamsSer {
|
|
1102
|
+
const parameters: Partial<SmoNoteBarParamsSer> = {};
|
|
1103
|
+
smoSerialize.serializedMergeNonDefault(SmoNoteBar.defaults,
|
|
1104
|
+
SmoNoteBar.parameterArray, this, parameters);
|
|
1105
|
+
parameters.ctor = 'SmoNoteBar';
|
|
1106
|
+
return parameters as SmoNoteBarParamsSer;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1026
1109
|
/**
|
|
1027
1110
|
* The persisted bits of {@link SmoDynamicTextParams}
|
|
1028
1111
|
* @category serialization
|
|
@@ -1205,6 +1288,7 @@ export const noteModifierDynamicCtorInit = () => {
|
|
|
1205
1288
|
SmoDynamicCtor['SmoGraceNote'] = (params: GraceNoteParams) => new SmoGraceNote(params);
|
|
1206
1289
|
SmoDynamicCtor['SmoArticulation'] = (params: SmoArticulationParameters) => new SmoArticulation(params);
|
|
1207
1290
|
SmoDynamicCtor['SmoLyric'] = (params: SmoLyricParams) => new SmoLyric(params);
|
|
1291
|
+
SmoDynamicCtor['SmoNoteBar'] = (params: SmoNoteBarParams) => new SmoNoteBar(params);
|
|
1208
1292
|
SmoDynamicCtor['SmoDynamicText'] = (params: SmoDynamicTextParams) => new SmoDynamicText(params);
|
|
1209
1293
|
SmoDynamicCtor['SmoTabNote'] = (params: SmoTabNoteParams) => new SmoTabNote(params);
|
|
1210
1294
|
SmoDynamicCtor['SmoClefChange'] = (params: SmoClefChangeParams) => new SmoClefChange(params);
|
package/src/smo/data/score.ts
CHANGED
|
@@ -722,6 +722,12 @@ export class SmoScore {
|
|
|
722
722
|
if (!isSmoScoreParams(params)) {
|
|
723
723
|
throw 'Bad score, missing params: ' + JSON.stringify(params, null, ' ');
|
|
724
724
|
}
|
|
725
|
+
if (params.staves.length === 1) {
|
|
726
|
+
const part = params.staves[0].partInfo;
|
|
727
|
+
if (part) {
|
|
728
|
+
part.expandMultimeasureRests = true;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
725
731
|
const score = new SmoScore(params);
|
|
726
732
|
score.textGroups = textGroups;
|
|
727
733
|
score.systemGroups = systemGroups;
|
|
@@ -950,8 +956,12 @@ export class SmoScore {
|
|
|
950
956
|
// immediately preceeding or post-ceding measure if it exists.
|
|
951
957
|
if (measureIndex < staff.measures.length) {
|
|
952
958
|
protomeasure = staff.measures[measureIndex];
|
|
959
|
+
const instrument = staff.getStaffInstrument(measureIndex);
|
|
960
|
+
protomeasure.lines = instrument.lines;
|
|
953
961
|
} else if (staff.measures.length) {
|
|
954
962
|
protomeasure = staff.measures[staff.measures.length - 1];
|
|
963
|
+
const instrument = staff.getStaffInstrument(staff.measures.length - 1);
|
|
964
|
+
protomeasure.lines = instrument.lines;
|
|
955
965
|
} else {
|
|
956
966
|
protomeasure = SmoMeasure.defaults;
|
|
957
967
|
}
|
|
@@ -124,9 +124,10 @@ export interface SmoScoreInfo {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
|
|
127
|
-
export type SmoScorePreferenceBool = 'autoPlay' | 'autoAdvance' | 'showPiano' | 'hideEmptyLines' | 'transposingScore';
|
|
127
|
+
export type SmoScorePreferenceBool = 'autoPlay' | 'autoAdvance' | 'showPiano' | 'hideEmptyLines' | 'transposingScore' | 'autoScrollPlayback';
|
|
128
128
|
export type SmoScorePreferenceNumber = 'defaultDupleDuration' | 'defaultTripleDuration';
|
|
129
|
-
export const SmoScorePreferenceBools: SmoScorePreferenceBool[] = ['autoPlay', 'autoAdvance', 'showPiano', 'hideEmptyLines',
|
|
129
|
+
export const SmoScorePreferenceBools: SmoScorePreferenceBool[] = ['autoPlay', 'autoAdvance', 'showPiano', 'hideEmptyLines',
|
|
130
|
+
'transposingScore', 'autoScrollPlayback'];
|
|
130
131
|
export const SmoScorePreferenceNumbers: SmoScorePreferenceNumber[] = ['defaultDupleDuration', 'defaultTripleDuration'];
|
|
131
132
|
/**
|
|
132
133
|
* Global score/program behavior preferences, see below for parameters
|
|
@@ -135,6 +136,7 @@ export const SmoScorePreferenceNumbers: SmoScorePreferenceNumber[] = ['defaultDu
|
|
|
135
136
|
export interface SmoScorePreferencesParams {
|
|
136
137
|
autoPlay: boolean;
|
|
137
138
|
autoAdvance: boolean;
|
|
139
|
+
autoScrollPlayback: boolean;
|
|
138
140
|
defaultDupleDuration: number;
|
|
139
141
|
defaultTripleDuration: number;
|
|
140
142
|
showPiano: boolean;
|
|
@@ -159,6 +161,7 @@ export class SmoScorePreferences {
|
|
|
159
161
|
defaultTripleDuration: number = 6144;
|
|
160
162
|
showPiano: boolean = false;
|
|
161
163
|
hideEmptyLines: boolean = false;
|
|
164
|
+
autoScrollPlayback: boolean = true;
|
|
162
165
|
transposingScore: boolean = false;
|
|
163
166
|
static get defaults(): SmoScorePreferencesParams {
|
|
164
167
|
return {
|
|
@@ -166,19 +169,24 @@ export class SmoScorePreferences {
|
|
|
166
169
|
autoAdvance: true,
|
|
167
170
|
defaultDupleDuration: 4096,
|
|
168
171
|
defaultTripleDuration: 6144,
|
|
172
|
+
autoScrollPlayback: true,
|
|
169
173
|
showPiano: false,
|
|
170
174
|
hideEmptyLines: false,
|
|
171
175
|
transposingScore: false
|
|
172
176
|
};
|
|
173
177
|
}
|
|
174
178
|
constructor(params: SmoScorePreferencesParams) {
|
|
175
|
-
if (params) {
|
|
179
|
+
if (params) {
|
|
176
180
|
SmoScorePreferenceBools.forEach((bb) => {
|
|
177
181
|
this[bb] = params[bb];
|
|
178
182
|
});
|
|
179
183
|
SmoScorePreferenceNumbers.forEach((nn) => {
|
|
180
184
|
this[nn] = params[nn];
|
|
181
185
|
});
|
|
186
|
+
// legacy, added later
|
|
187
|
+
if (typeof(params.autoScrollPlayback) === 'undefined') {
|
|
188
|
+
this.autoScrollPlayback = true;
|
|
189
|
+
}
|
|
182
190
|
}
|
|
183
191
|
}
|
|
184
192
|
serialize(): SmoScorePreferencesParams {
|
|
@@ -158,7 +158,8 @@ export interface SmoInstrumentParams {
|
|
|
158
158
|
/**
|
|
159
159
|
* future, can be used to set sample
|
|
160
160
|
*/
|
|
161
|
-
mutes?: string,
|
|
161
|
+
mutes?: string,
|
|
162
|
+
lines: number
|
|
162
163
|
}
|
|
163
164
|
|
|
164
165
|
/**
|
|
@@ -174,8 +175,8 @@ export interface SmoInstrumentParamsSer extends SmoInstrumentParams {
|
|
|
174
175
|
function isSmoInstrumentParamsSer(params: Partial<SmoInstrumentParamsSer>): params is SmoInstrumentParamsSer {
|
|
175
176
|
return params?.ctor === 'SmoInstrument';
|
|
176
177
|
}
|
|
177
|
-
export type SmoInstrumentNumParamType = 'keyOffset' | 'midichannel' | 'midiport' | 'midiInstrument';
|
|
178
|
-
export const SmoInstrumentNumParams: SmoInstrumentNumParamType[] = ['keyOffset', 'midichannel', 'midiport', 'midiInstrument'];
|
|
178
|
+
export type SmoInstrumentNumParamType = 'keyOffset' | 'midichannel' | 'midiport' | 'midiInstrument' | 'lines';
|
|
179
|
+
export const SmoInstrumentNumParams: SmoInstrumentNumParamType[] = ['keyOffset', 'midichannel', 'midiport', 'midiInstrument', 'lines'];
|
|
179
180
|
export type SmoInstrumentStringParamType = 'instrumentName' | 'abbreviation' | 'family' | 'instrument';
|
|
180
181
|
export const SmoInstrumentStringParams: SmoInstrumentStringParamType[] = ['instrumentName', 'abbreviation', 'family', 'instrument'];
|
|
181
182
|
/**
|
|
@@ -187,7 +188,8 @@ export const SmoInstrumentStringParams: SmoInstrumentStringParamType[] = ['instr
|
|
|
187
188
|
*/
|
|
188
189
|
export class SmoInstrument extends StaffModifierBase {
|
|
189
190
|
static get attributes() {
|
|
190
|
-
return ['startSelector', 'endSelector',
|
|
191
|
+
return ['startSelector', 'endSelector',
|
|
192
|
+
'keyOffset', 'midichannel', 'midiport', 'instrumentName', 'abbreviation', 'instrument', 'family', 'lines'];
|
|
191
193
|
}
|
|
192
194
|
startSelector: SmoSelector;
|
|
193
195
|
endSelector: SmoSelector;
|
|
@@ -196,6 +198,7 @@ export class SmoInstrument extends StaffModifierBase {
|
|
|
196
198
|
keyOffset: number = 0;
|
|
197
199
|
clef: Clef = 'treble';
|
|
198
200
|
midiInstrument: number = 1;
|
|
201
|
+
lines: number = 5;
|
|
199
202
|
midichannel: number;
|
|
200
203
|
midiport: number;
|
|
201
204
|
family: string;
|
|
@@ -214,7 +217,8 @@ export class SmoInstrument extends StaffModifierBase {
|
|
|
214
217
|
midiInstrument: 1,
|
|
215
218
|
midiport: 0,
|
|
216
219
|
startSelector: SmoSelector.default,
|
|
217
|
-
endSelector: SmoSelector.default
|
|
220
|
+
endSelector: SmoSelector.default,
|
|
221
|
+
lines: 5
|
|
218
222
|
}));
|
|
219
223
|
}
|
|
220
224
|
static get defaultOscillatorParam(): SmoOscillatorInfo {
|
|
@@ -241,6 +245,11 @@ export class SmoInstrument extends StaffModifierBase {
|
|
|
241
245
|
} else {
|
|
242
246
|
name = (params as any).instrument;
|
|
243
247
|
}
|
|
248
|
+
if (typeof ((params as any).lines) === 'undefined') {
|
|
249
|
+
this.lines = 5;
|
|
250
|
+
} else {
|
|
251
|
+
this.lines = params.lines;
|
|
252
|
+
}
|
|
244
253
|
this.instrumentName = name;
|
|
245
254
|
this.family = params.family;
|
|
246
255
|
this.instrument = params.instrument;
|
|
@@ -380,6 +380,7 @@ export class SmoSystemStaff implements SmoObjectParams {
|
|
|
380
380
|
curInstrumentIndex += 1;
|
|
381
381
|
}
|
|
382
382
|
measure.transposeIndex = instrumentAr[curInstrumentIndex].instrument.keyOffset;
|
|
383
|
+
measure.lines = instrumentAr[curInstrumentIndex].instrument.lines;
|
|
383
384
|
params.measures.push(measure);
|
|
384
385
|
});
|
|
385
386
|
if (jsonObj.modifiers) {
|
|
@@ -416,6 +417,15 @@ export class SmoSystemStaff implements SmoObjectParams {
|
|
|
416
417
|
}
|
|
417
418
|
mod.associatedStaff = to; // this.staffId will remap to 'to' value
|
|
418
419
|
});
|
|
420
|
+
this.textBrackets.forEach((mod: SmoStaffTextBracket) => {
|
|
421
|
+
if (mod.startSelector.staff === from) {
|
|
422
|
+
mod.startSelector.staff = to;
|
|
423
|
+
}
|
|
424
|
+
if (mod.endSelector.staff === from) {
|
|
425
|
+
mod.endSelector.staff = to;
|
|
426
|
+
}
|
|
427
|
+
mod.associatedStaff = to; // this.staffId will remap to 'to' value
|
|
428
|
+
});
|
|
419
429
|
}
|
|
420
430
|
updateMeasureFormatsForPart() {
|
|
421
431
|
this.measures.forEach((measure, mix) => {
|
|
@@ -453,6 +463,7 @@ export class SmoSystemStaff implements SmoObjectParams {
|
|
|
453
463
|
const tabStave: SmoTabStave | undefined = this.getTabStaveForMeasure(SmoSelector.fromMeasure(measure));
|
|
454
464
|
measure.transposeToOffset(entry.instrument.keyOffset, targetKey, entry.instrument.clef);
|
|
455
465
|
measure.transposeIndex = entry.instrument.keyOffset;
|
|
466
|
+
measure.lines = entry.instrument.lines;
|
|
456
467
|
measure.keySignature = targetKey;
|
|
457
468
|
measure.setClef(entry.instrument.clef);
|
|
458
469
|
}
|
|
@@ -4,6 +4,7 @@ import { SmoSelection, SmoSelector } from './selections';
|
|
|
4
4
|
import { SmoNote } from '../data/note';
|
|
5
5
|
import { SmoMeasure, SmoVoice } from '../data/measure';
|
|
6
6
|
import { StaffModifierBase } from '../data/staffModifiers';
|
|
7
|
+
import { SmoLyric } from '../data/noteModifiers';
|
|
7
8
|
import {SmoTuplet, SmoTupletTree, SmoTupletTreeParams} from '../data/tuplet';
|
|
8
9
|
import { SmoMusic } from '../data/music';
|
|
9
10
|
import { SvgHelpers } from '../../render/sui/svgHelpers';
|
|
@@ -131,6 +132,14 @@ export class PasteBuffer {
|
|
|
131
132
|
const keyOffset = -1 * selection.measure.transposeIndex;
|
|
132
133
|
const destKey = SmoMusic.vexKeySignatureTranspose(originalKey, keyOffset).toLocaleLowerCase();
|
|
133
134
|
const note = SmoNote.transpose(SmoNote.clone(selection.note),[], keyOffset, selection.measure.keySignature, destKey) as SmoNote;
|
|
135
|
+
const chords: SmoLyric[] = note.getChords();
|
|
136
|
+
chords.forEach((chord) => {
|
|
137
|
+
note.removeLyric(chord);
|
|
138
|
+
});
|
|
139
|
+
chords.forEach((chord) => {
|
|
140
|
+
const nchord = SmoLyric.transposeChordToKey(chord, keyOffset, selection.measure.keySignature, destKey);
|
|
141
|
+
note.addLyric(nchord);
|
|
142
|
+
});
|
|
134
143
|
const pasteNote: PasteNote = {
|
|
135
144
|
selector,
|
|
136
145
|
note,
|
|
@@ -517,7 +526,47 @@ export class PasteBuffer {
|
|
|
517
526
|
}
|
|
518
527
|
serializedMeasure.voices = voices;
|
|
519
528
|
}
|
|
520
|
-
|
|
529
|
+
pasteChords(selector: SmoSelector) {
|
|
530
|
+
if (this.notes.length < 1) {
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
if (!this.score) {
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
let srcTick = 0;
|
|
537
|
+
let destTick = 0;
|
|
538
|
+
let srcIndex = 0;
|
|
539
|
+
let selection = SmoSelection.noteSelection(this.score!, selector.staff, selector.measure, selector.voice, selector.tick);
|
|
540
|
+
while (selection && selection.note && srcIndex < this.notes.length) {
|
|
541
|
+
const srcNote = this.notes[srcIndex].note;
|
|
542
|
+
const chords = srcNote.getChords();
|
|
543
|
+
if (selection && selection.note) {
|
|
544
|
+
const destNote = selection.note;
|
|
545
|
+
if (chords.length) {
|
|
546
|
+
chords.forEach((chord) => {
|
|
547
|
+
destNote.removeLyric(chord);
|
|
548
|
+
});
|
|
549
|
+
chords.forEach((chord) => {
|
|
550
|
+
if (selection) {
|
|
551
|
+
const nchord = SmoLyric.transposeChordToKey(
|
|
552
|
+
chord, selection.measure.transposeIndex,this.notes[srcIndex].originalKey, selection.measure.keySignature);
|
|
553
|
+
destNote.addLyric(nchord);
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
srcTick += srcNote.tickCount;
|
|
558
|
+
while (selection && selection.note && destTick < srcTick) {
|
|
559
|
+
destTick += selection.note.tickCount;
|
|
560
|
+
if (selection && selection.note) {
|
|
561
|
+
const curSelector = selection.selector;
|
|
562
|
+
selection = SmoSelection.nextNoteSelection(this.score,
|
|
563
|
+
curSelector.staff, curSelector.measure, curSelector.voice, curSelector.tick);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
srcIndex += 1;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
521
570
|
pasteSelections(selector: SmoSelector) {
|
|
522
571
|
let i = 0;
|
|
523
572
|
if (this.notes.length < 1) {
|
|
@@ -87,11 +87,26 @@ export class SmoOperation {
|
|
|
87
87
|
});
|
|
88
88
|
});
|
|
89
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Move a single stave up or down one. If last down, move to first.
|
|
92
|
+
* If first up, move to last
|
|
93
|
+
* @param score
|
|
94
|
+
* @param selection
|
|
95
|
+
* @param index
|
|
96
|
+
*/
|
|
90
97
|
static moveStaffUpDown(score: SmoScore, selection: SmoSelection, index: number) {
|
|
91
98
|
const index1 = selection.selector.staff;
|
|
92
99
|
const index2 = selection.selector.staff + index;
|
|
93
100
|
if (index2 < score.staves.length && index2 >= 0) {
|
|
94
101
|
score.swapStaves(index1, index2);
|
|
102
|
+
} else if (index2 === score.staves.length) {
|
|
103
|
+
for (let i = 0; i < (score.staves.length - 1); ++i) {
|
|
104
|
+
score.swapStaves((score.staves.length - 1) - i, (score.staves.length - 1) - (i + 1));
|
|
105
|
+
}
|
|
106
|
+
} else if (index2 < 0) {
|
|
107
|
+
for (let i = 0; i < (score.staves.length - 1); ++i) {
|
|
108
|
+
score.swapStaves(i, i + 1);
|
|
109
|
+
}
|
|
95
110
|
}
|
|
96
111
|
}
|
|
97
112
|
|
|
@@ -13,7 +13,7 @@ import { SuiDialogNotifier, SuiBaseComponentParams } from './components/baseComp
|
|
|
13
13
|
* Buttons with ending or repeat functions
|
|
14
14
|
* @returns SuiButtonArrayParameters
|
|
15
15
|
*/
|
|
16
|
-
const endingsButtonFactory: getButtonsFcn = () => {
|
|
16
|
+
export const endingsButtonFactory: getButtonsFcn = () => {
|
|
17
17
|
const params: SuiButtonArrayParameters = {
|
|
18
18
|
label: 'Measure Endings',
|
|
19
19
|
rows: [{
|
|
@@ -38,7 +38,12 @@ export class SuiInstrumentAdapter extends SuiComponentAdapter {
|
|
|
38
38
|
this.view.changeInstrument(this.instrument, this.selections);
|
|
39
39
|
this.instrument = new SmoInstrument(this.instrument);
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
get lines() {
|
|
42
|
+
return this.instrument.lines;
|
|
43
|
+
}
|
|
44
|
+
set lines(value: number) {
|
|
45
|
+
this.writeNumParam('lines', value);
|
|
46
|
+
}
|
|
42
47
|
get transposeIndex() {
|
|
43
48
|
return this.instrument.keyOffset;
|
|
44
49
|
}
|
|
@@ -108,6 +113,11 @@ export class SuiInstrumentDialog extends SuiDialogAdapterBase<SuiInstrumentAdapt
|
|
|
108
113
|
label: 'Instrument Properties',
|
|
109
114
|
elements:
|
|
110
115
|
[{
|
|
116
|
+
smoName: 'lines',
|
|
117
|
+
defaultValue: 5,
|
|
118
|
+
control: 'SuiRockerComponent',
|
|
119
|
+
label: 'Staff lines (1-5)'
|
|
120
|
+
}, {
|
|
111
121
|
smoName: 'transposeIndex',
|
|
112
122
|
defaultValue: 0,
|
|
113
123
|
control: 'SuiRockerComponent',
|
|
@@ -44,6 +44,13 @@ export class SuiScorePreferencesAdapter extends SuiComponentAdapter {
|
|
|
44
44
|
this.preferences.showPiano = value;
|
|
45
45
|
this.view.updateScorePreferences(this.preferences);
|
|
46
46
|
}
|
|
47
|
+
get autoScrollPlayback(): boolean {
|
|
48
|
+
return this.preferences.autoScrollPlayback;
|
|
49
|
+
}
|
|
50
|
+
set autoScrollPlayback(value: boolean) {
|
|
51
|
+
this.preferences.autoScrollPlayback = value;
|
|
52
|
+
this.view.updateScorePreferences(this.preferences);
|
|
53
|
+
}
|
|
47
54
|
get hideEmptyLines(): boolean {
|
|
48
55
|
return this.preferences.hideEmptyLines;
|
|
49
56
|
}
|
|
@@ -102,6 +109,10 @@ export class SuiScorePreferencesDialog extends SuiDialogAdapterBase<SuiScorePref
|
|
|
102
109
|
smoName: 'autoPlay',
|
|
103
110
|
control: 'SuiToggleComponent',
|
|
104
111
|
label: 'Auto-play sounds for note entry'
|
|
112
|
+
}, {
|
|
113
|
+
smoName: 'autoScrollPlayback',
|
|
114
|
+
control: 'SuiToggleComponent',
|
|
115
|
+
label: 'Auto-Scroll Playback'
|
|
105
116
|
}, {
|
|
106
117
|
smoName: 'showPiano',
|
|
107
118
|
control: 'SuiToggleComponent',
|