From 2ac40c176cf67cd7f6bc0d54f0856788e9b3d1cc Mon Sep 17 00:00:00 2001 From: AaronDavidNewman Date: Mon, 27 May 2024 10:58:16 -0500 Subject: [PATCH] formatting flags, beam group formatting After loading a midi file, auto-format the beam groups. --- src/render/sui/formatter.ts | 3 +++ src/render/sui/scoreView.ts | 4 ++-- src/render/sui/scoreViewOperations.ts | 15 +++++++++++++++ src/smo/data/systemStaff.ts | 6 +++--- src/smo/midi/midiToSmo.ts | 2 ++ src/smo/xform/beamers.ts | 11 ++++++++++- src/smo/xform/operations.ts | 23 +++++++++++++++++++++++ 7 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/render/sui/formatter.ts b/src/render/sui/formatter.ts index 270b4339..7b7c9085 100644 --- a/src/render/sui/formatter.ts +++ b/src/render/sui/formatter.ts @@ -454,6 +454,9 @@ export class SuiLayoutFormatter { // TODO: Consider engraving font and adjust grace note size? noteWidth += (headWidth + vexGlyph.dimensions.noteHead.spacingRight) * note.graceNotes.length; noteWidth += dotWidth * dots + vexGlyph.dimensions.dot.spacingRight * dots; + if (!note.isRest() && note.endBeam) { + noteWidth += vexGlyph.dimensions.flag.width; + } note.pitches.forEach((pitch) => { const keyAccidental = SmoMusic.getAccidentalForKeySignature(pitch, smoMeasure.keySignature); const accidentals = tmObj.accidentalArray.filter((ar) => diff --git a/src/render/sui/scoreView.ts b/src/render/sui/scoreView.ts index 86c3cb0c..bf03bfe5 100644 --- a/src/render/sui/scoreView.ts +++ b/src/render/sui/scoreView.ts @@ -109,11 +109,11 @@ export abstract class SuiScoreView { * await on the full update of the score, also resetting the viewport (to reflect layout changes) * @returns */ - refreshViewport(): Promise { + async refreshViewport(): Promise { this.renderer.preserveScroll(); this.renderer.setViewport(); this.renderer.setRefresh(); - return this.renderer.renderPromise(); + await this.renderer.renderPromise(); } handleScrollEvent(scrollLeft: number, scrollTop: number) { this.tracker.scroller.handleScroll(scrollLeft, scrollTop); diff --git a/src/render/sui/scoreViewOperations.ts b/src/render/sui/scoreViewOperations.ts index b7ce1946..cf14d60f 100644 --- a/src/render/sui/scoreViewOperations.ts +++ b/src/render/sui/scoreViewOperations.ts @@ -954,6 +954,21 @@ export class SuiScoreViewOperations extends SuiScoreView { this._renderChangedMeasures(measureSelections); await this.renderer.updatePromise(); } + async clearAllBeams(): Promise { + this._undoScore('clearAllBeams'); + SmoOperation.clearAllBeamGroups(this.score); + SmoOperation.clearAllBeamGroups(this.storeScore); + await this.awaitRender(); + } + async clearSelectedBeams() { + const selections = this.tracker.selections; + const measures = SmoSelection.getMeasureList(selections); + const altSelections = this._getEquivalentSelections(selections); + SmoOperation.clearBeamGroups(this.score, selections); + SmoOperation.clearBeamGroups(this.storeScore, altSelections); + this._renderChangedMeasures(measures); + await this.renderer.updatePromise(); + } /** * toggle the 'end beam' flag for selected notes * @returns diff --git a/src/smo/data/systemStaff.ts b/src/smo/data/systemStaff.ts index c3330575..85d51af8 100644 --- a/src/smo/data/systemStaff.ts +++ b/src/smo/data/systemStaff.ts @@ -625,14 +625,14 @@ export class SmoSystemStaff implements SmoObjectParams { || SmoSelector.gteq(tb.endSelector, nb.startSelector) || tb.position !== nb.position); brackets.push(new SmoStaffTextBracket(bracketParams)); - + this.textBrackets = brackets; } removeTextBracket(bracketParams: SmoStaffTextBracket) { const nb = new SmoStaffTextBracket(bracketParams); const brackets = this.textBrackets.filter((tb) => SmoSelector.lteq(tb.startSelector, nb.startSelector) || SmoSelector.gteq(tb.endSelector, nb.startSelector) || tb.position !== nb.position); - brackets.push(new SmoStaffTextBracket(bracketParams)); - this.textBrackets = brackets; + brackets.push(new SmoStaffTextBracket(bracketParams)); + this.textBrackets = brackets; } getTextBracketsStartingAt(selector: SmoSelector) { return this.textBrackets.filter((tb) => SmoSelector.eq(tb.startSelector, selector)); diff --git a/src/smo/midi/midiToSmo.ts b/src/smo/midi/midiToSmo.ts index c9153abe..e1401ebb 100644 --- a/src/smo/midi/midiToSmo.ts +++ b/src/smo/midi/midiToSmo.ts @@ -15,6 +15,7 @@ import { SmoLayoutManager } from "../data/scoreModifiers"; import { SmoTie } from "../data/staffModifiers"; import { SmoSystemStaff } from "../data/systemStaff"; import { SmoTuplet } from "../data/tuplet"; +import { SmoOperation } from "../xform/operations"; export type MidiEventType = 'text' | 'copyrightNotice' | 'trackName' | 'instrumentName' | 'lyrics' | 'marker' | 'cuePoint' | 'channelPrefix' | 'portPrefix' | 'endOfTrack' | 'setTempo' | 'smpteOffset' | 'timeSignature' | 'keySignature' | @@ -656,6 +657,7 @@ export class MidiToSmo { // if no scale given in score, default to something small. layoutDefaults.globalLayout.svgScale = 0.65; layoutDefaults.globalLayout.zoomScale = 1.5; + SmoOperation.clearAllBeamGroups(rv); return rv; } } \ No newline at end of file diff --git a/src/smo/xform/beamers.ts b/src/smo/xform/beamers.ts index fd11373b..ee3925fe 100644 --- a/src/smo/xform/beamers.ts +++ b/src/smo/xform/beamers.ts @@ -57,6 +57,7 @@ export class SmoBeamer { } measure: SmoMeasure; duration: number; + measureDuration: number; meterNumbers: number[]; beamBeats: number; skipNext: number; @@ -65,6 +66,7 @@ export class SmoBeamer { this.measure = measure; this._removeVoiceBeam(measure, voice); this.duration = 0; + this.measureDuration = 0; this.meterNumbers = [measure.timeSignature.actualBeats, measure.timeSignature.beatDuration]; // beam on 1/4 notes in most meter, triple time dotted quarter this.beamBeats = 2 * 2048; @@ -139,6 +141,7 @@ export class SmoBeamer { beamNote(tickmap: TickMap, index: number, note: SmoNote) { this.beamBeats = note.beamBeats; this.duration += tickmap.deltaMap[index]; + this.measureDuration += tickmap.deltaMap[index]; if (note.noteType === '/') { this._completeGroup(tickmap.voice); this._advanceGroup(); @@ -193,7 +196,13 @@ export class SmoBeamer { this._advanceGroup(); } } - + // If we are aligned to a beat on the measure, and we are in common time + if (this.currentGroup.length > 1 && this.measure.timeSignature.beatDuration === 4 && + this.measureDuration % 4096 === 0) { + this._completeGroup(tickmap.voice); + this._advanceGroup(); + return; + } if (this.duration === this.beamBeats) { this._completeGroup(tickmap.voice); this._advanceGroup(); diff --git a/src/smo/xform/operations.ts b/src/smo/xform/operations.ts index d8a921d2..4d04d04e 100644 --- a/src/smo/xform/operations.ts +++ b/src/smo/xform/operations.ts @@ -639,6 +639,29 @@ export class SmoOperation { } } } + static clearAllBeamGroups(score: SmoScore) { + score.staves.forEach((ss) => { + ss.measures.forEach((mm) => { + mm.voices.forEach((vv) => { + const triple = mm.timeSignature.actualBeats % 3 === 0; + vv.notes.forEach((note) => { + note.beamBeats = triple ? score.preferences.defaultTripleDuration : score.preferences.defaultDupleDuration; + note.endBeam = false; + }); + }); + }); + }); + } + static clearBeamGroups(score: SmoScore, selections: SmoSelection[]) { + selections.forEach((ss) => { + if (ss.note) { + const triple = ss.measure.timeSignature.actualBeats % 3 === 0; + const note = ss.note; + note.beamBeats = triple ? score.preferences.defaultTripleDuration : score.preferences.defaultDupleDuration; + note.endBeam = false; + } + }); + } static toggleBeamDirection(selections: SmoSelection[]) { const note0 = selections[0].note as SmoNote;