Skip to content

Commit

Permalink
implement clefNote
Browse files Browse the repository at this point in the history
  • Loading branch information
AaronDavidNewman committed Feb 25, 2024
1 parent b195f6a commit 5277b3d
Show file tree
Hide file tree
Showing 11 changed files with 290 additions and 20 deletions.
5 changes: 3 additions & 2 deletions src/application/exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ import { SuiTieAttributesDialog } from '../ui/dialogs/tie';
import { SuiVoltaAttributeDialog } from '../ui/dialogs/volta';
import { SuiHairpinAttributesDialog } from '../ui/dialogs/hairpin';
import { SuiStaffGroupDialog } from '../ui/dialogs/staffGroup';
import { SuiScoreArpeggioDialog } from '../ui/dialogs/arpeggio'
import { SuiArpeggioDialog } from '../ui/dialogs/arpeggio';
import { SuiClefChangeDialog } from '../ui/dialogs/clefChange';
import { SuiPartInfoDialog } from '../ui/dialogs/partInfo';
import { SuiLoadMxmlDialog, SuiLoadFileDialog,
/* SuiLoadActionsDialog, SuiSaveActionsDialog, */
Expand Down Expand Up @@ -196,7 +197,7 @@ export const Smo = {
SuiFontComponent, SuiTextInPlace, SuiLyricComponent, SuiChordComponent, SuiDragText,
SuiNoteTextComponent, SuiTextBlockComponent, SuiTextInputComponent,
SuiDynamicModifierDialog, CheckboxDropdownComponent, TieMappingComponent, StaffAddRemoveComponent,
StaffCheckComponent, TextCheckComponent, SuiScoreArpeggioDialog,
StaffCheckComponent, TextCheckComponent, SuiArpeggioDialog, SuiClefChangeDialog,
SuiXhrLoader,PromiseHelpers,
// Rendering components
SuiPiano, layoutDebug, SuiScoreView,SuiScroller, SvgHelpers, SuiMapper, SuiScoreRender,
Expand Down
2 changes: 2 additions & 0 deletions src/common/vex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Vex as SmoVex, Note as VexNote, StaveNote as VexStaveNote, StemmableNot
Stave as VexStave, StaveModifierPosition as VexStaveModifierPosition,
Font as VexFont, FontInfo as VexFontInfo, FontStyle as VexFontStyle, FontWeight as VexFontWeight,
TupletOptions as VexTupletOptions, Curve as VexCurve, StaveTie as VexStaveTie,
ClefNote as VexClefNote,
Music as VexMusic, ChordSymbol as VexChordSymbol, ChordSymbolBlock as VexChordSymbolBlock } from "vexflow_smoosic";

/**
Expand Down Expand Up @@ -37,6 +38,7 @@ export type StaveText = VexStaveText;
export type Stave = VexStave;
export type Curve = VexCurve;
export type StaveTie = VexStaveTie;
export type ClefNote = VexClefNote;
export type StaveModifierPosition = VexStaveModifierPosition;


Expand Down
3 changes: 1 addition & 2 deletions src/render/sui/formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ export class SuiLayoutFormatter {
let isPickup = false;
// Keep running tab of accidental widths for justification
const contextMap: Record<number, SuiTickContext> = {};
let forceClefCount = 0;
let measureToSkip = false;
let maxColumnStartX = 0;
measures.forEach((measure) => {
Expand All @@ -185,7 +184,7 @@ export class SuiLayoutFormatter {
s.keySigLast = SmoMusic.vexKeySignatureTranspose(measureToLeft.keySignature, 0);
s.tempoLast = measureToLeft.getTempo();
s.timeSigLast = measureToLeft.timeSignature;
s.clefLast = measureToLeft.clef;
s.clefLast = measureToLeft.getLastClef();
this.calculateBeginningSymbols(systemIndex, measure, s.clefLast, s.keySigLast, s.timeSigLast, s.tempoLast);
const startX = SuiLayoutFormatter.estimateStartSymbolWidth(measure);
measure.svg.adjX = startX;
Expand Down
29 changes: 27 additions & 2 deletions src/render/sui/scoreViewOperations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { SmoSystemGroup, SmoPageLayout, SmoGlobalLayout, SmoLayoutManager, SmoAu
SmoScorePreferences, SmoScoreInfo } from '../../smo/data/scoreModifiers';
import { SmoTextGroup } from '../../smo/data/scoreText';
import { SmoDynamicText, SmoNoteModifierBase, SmoGraceNote, SmoArticulation,
SmoOrnament, SmoLyric, SmoMicrotone, SmoArpeggio, SmoArpeggioType } from '../../smo/data/noteModifiers';
SmoOrnament, SmoLyric, SmoMicrotone, SmoArpeggio, SmoArpeggioType, SmoClefChange } from '../../smo/data/noteModifiers';
import { SmoTempoText, SmoVolta, SmoBarline, SmoRepeatSymbol, SmoRehearsalMark, SmoMeasureFormat, TimeSignature } from '../../smo/data/measureModifiers';
import { UndoBuffer, SmoUndoable } from '../../smo/xform/undo';
import { SmoOperation } from '../../smo/xform/operations';
Expand Down Expand Up @@ -211,7 +211,7 @@ export class SuiScoreViewOperations extends SuiScoreView {
async addRemoveArpeggio(code: SmoArpeggioType) {
const selections = this.tracker.selections;
const altSelections = this._getEquivalentSelections(selections);
const measureSelections = this._undoTrackerMeasureSelections('add/remove microtone');
const measureSelections = this._undoTrackerMeasureSelections('add/remove arpeggio');
[selections, altSelections].forEach((selType) => {
selType.forEach((sel) => {
if (sel.note) {
Expand All @@ -226,6 +226,31 @@ export class SuiScoreViewOperations extends SuiScoreView {
this._renderChangedMeasures(measureSelections);
await this.renderer.updatePromise()
}
/**
* A clef change mid-measure (clefNote)
* @param clef
*/
async addRemoveClefChange(clef: SmoClefChange) {
const selections = [this.tracker.selections[0]];
const altSelections = this._getEquivalentSelections(selections);
const measureSelections = this._undoTrackerMeasureSelections('add/remove clef change');
[selections, altSelections].forEach((selType) => {
selType.forEach((sel) => {
if (sel.note) {
const measureClef = sel.measure.clef;
// If the clef is the same as measure clef, remove any clef change from Note
if (measureClef === clef.clef) {
sel.note.clefNote = null;
} else {
sel.note.clefNote = clef;
}
sel.measure.updateClefChangeNotes();
}
});
});
this._renderChangedMeasures(measureSelections);
await this.renderer.updatePromise()
}
/**
* Modify the dynamics assoicated with the specific selection
* @param selection
Expand Down
8 changes: 6 additions & 2 deletions src/render/vex/vxMeasure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import { VexFlow, Stave,StemmableNote, Note, Beam, Tuplet, Voice,
Formatter, Accidental, Annotation, StaveNoteStruct, StaveText, StaveModifier,
createStaveText, renderDynamics, applyStemDirection,
getVexNoteParameters, defaultNoteScale, defaultCueScale, getVexTuplets,
createStave, createVoice, getOrnamentGlyph, getSlashGlyph, getRepeatBar, getMultimeasureRest,
addChordGlyph } from '../../common/vex';
createStave, createVoice, getOrnamentGlyph, getSlashGlyph, getRepeatBar, getMultimeasureRest
} from '../../common/vex';

import { VxMeasureIf, VexNoteModifierIf, VxNote } from './vxNote';
const VF = VexFlow;
Expand Down Expand Up @@ -260,6 +260,10 @@ export class VxMeasure implements VxMeasureIf {
const vexNote = this.createVexNote(smoNote, i, voiceIx);
this.noteToVexMap[smoNote.attrs.id] = vexNote.noteData.staveNote;
this.vexNotes.push(vexNote.noteData.staveNote);
if (vexNote.noteData.smoNote.clefNote) {
const cf = new VF.ClefNote(vexNote.noteData.smoNote.clefNote.clef, 'small');
this.voiceNotes.push(cf);
}
this.voiceNotes.push(vexNote.noteData.staveNote);
if (isNaN(smoNote.ticks.numerator) || isNaN(smoNote.ticks.denominator)
|| isNaN(smoNote.ticks.remainder)) {
Expand Down
64 changes: 63 additions & 1 deletion src/smo/data/measure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
id: getId().toString(),
type: 'SmoMeasure'
};
this.updateClefChangeNotes();
}

// @internal
Expand Down Expand Up @@ -455,7 +456,52 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
static timeSigEqual(o1: TimeSignature, o2: TimeSignature) {
return o1.timeSignature === o2.timeSignature && o1.useSymbol === o2.useSymbol;
}
/**
* If there is a clef change mid-measure, update the actual clefs of the notes
* so they display correctly.
*/
updateClefChangeNotes() {
let changed = false;
let curTick = 0;
let clefChange = this.clef;
for (var i = 0; i < this.voices.length; ++i) {
const voice = this.voices[i];
curTick = 0;
for (var j = 0; j < voice.notes.length; ++j) {
const smoNote = voice.notes[j];
smoNote.clef = this.clef;
if (smoNote.clefNote && smoNote.clefNote.clef !== this.clef) {
clefChange = smoNote.clefNote.clef;
curTick += smoNote.tickCount;
changed = true;
break;
}
curTick += smoNote.tickCount;
}
if (changed) {
break;
}
}
if (!changed) {
return;
}
// clefChangeTick is where the change goes. We only support
// one per measure, others are ignored.
const clefChangeTick = curTick;

for (var i = 0; i < this.voices.length; ++i) {
const voice = this.voices[i];
curTick = 0;
for (var j = 0; j < voice.notes.length; ++j) {
const smoNote = voice.notes[j];
const noteTicks = smoNote.tickCount;
if (curTick + noteTicks >= clefChangeTick) {
smoNote.clef = clefChange;
}
curTick += noteTicks;
}
}
}
/**
* @internal
* @returns column mapped parameters, serialized. caller will
Expand Down Expand Up @@ -490,6 +536,7 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {

this.voices.forEach((voice) => {
const obj: any = {

notes: []
};
voice.notes.forEach((note) => {
Expand Down Expand Up @@ -1250,7 +1297,22 @@ export class SmoMeasure implements SmoMeasureParams, TickMappable {
});
});
}

/**
* Get the clef that this measure ends with.
* @returns
*/
getLastClef() {
for (var i = 0; i < this.voices.length; ++i) {
const voice = this.voices[i];
for (var j = 0; j < voice.notes.length; ++j) {
const note = voice.notes[j];
if (note.clefNote && note.clefNote.clef !== this.clef) {
return note.clefNote.clef;
}
}
}
return this.clef;
}
isRest() {
let i = 0;
for (i = 0; i < this.voices.length; ++i) {
Expand Down
20 changes: 15 additions & 5 deletions src/smo/data/note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
* @module /smo/data/note
*/
import { smoSerialize } from '../../common/serializationHelpers';
import { SmoNoteModifierBase, SmoArticulation, SmoLyric, SmoGraceNote, SmoMicrotone, SmoOrnament, SmoDynamicText, SmoArpeggio, SmoArticulationParametersSer, GraceNoteParamsSer, SmoOrnamentParamsSer, SmoMicrotoneParamsSer } from './noteModifiers';
import { SmoNoteModifierBase, SmoArticulation, SmoLyric, SmoGraceNote, SmoMicrotone, SmoOrnament, SmoDynamicText,
SmoArpeggio, SmoArticulationParametersSer, GraceNoteParamsSer, SmoOrnamentParamsSer, SmoMicrotoneParamsSer,
SmoClefChangeParamsSer, SmoClefChange } from './noteModifiers';
import { SmoMusic } from './music';
import { Ticks, Pitch, SmoAttrs, Transposable, PitchLetter, SvgBox, getId } from './common';
import { FontInfo, vexCanonicalNotes } from '../../common/vex';
Expand Down Expand Up @@ -125,7 +127,7 @@ export interface SmoNoteParams {
/**
* indicates this note goes with a clef change
*/
clefNote: string | null
clefNote: SmoClefChangeParamsSer
}

/**
Expand Down Expand Up @@ -212,7 +214,7 @@ export interface SmoNoteParamsSer {
/**
* indicates this note goes with a clef change
*/
clefNote: string | null
clefNote? : SmoClefChangeParamsSer
}
function isSmoNoteParamsSer(params: Partial<SmoNoteParamsSer>): params is SmoNoteParamsSer {
if (params.ctor && params.ctor === 'SmoNote') {
Expand All @@ -239,7 +241,9 @@ export class SmoNote implements Transposable {
NoteBooleanParams.forEach((param) => {
this[param] = params[param] ? params[param] : defs[param];
});
this.clefNote = params.clefNote;
if (params.clefNote) {
this.clefNote = new SmoClefChange(params.clefNote);
}
const ticks = params.ticks ? params.ticks : defs.ticks;
const pitches = params.pitches ? params.pitches : defs.pitches;
this.ticks = JSON.parse(JSON.stringify(ticks));
Expand Down Expand Up @@ -267,7 +271,7 @@ export class SmoNote implements Transposable {
noteHead: string = '';
arpeggio?: SmoArpeggio;
clef: string = 'treble';
clefNote: string | null = null;
clefNote: SmoClefChange | null = null;
graceNotes: SmoGraceNote[] = [];
noteType: NoteType = 'n';
fillStyle: string = '';
Expand Down Expand Up @@ -807,6 +811,9 @@ export class SmoNote implements Transposable {
serialize(): SmoNoteParamsSer {
var params: Partial<SmoNoteParamsSer> = { ctor: 'SmoNote' };
smoSerialize.serializedMergeNonDefault(SmoNote.defaults, SmoNote.parameterArray, this, params);
if (this.clefNote) {
params.clefNote = this.clefNote.serialize();
}
if (params.ticks) {
params.ticks = JSON.parse(JSON.stringify(params.ticks));
}
Expand Down Expand Up @@ -857,6 +864,9 @@ export class SmoNote implements Transposable {
if (jsonObj.arpeggio) {
note.arpeggio = SmoNoteModifierBase.deserialize(jsonObj.arpeggio);
}
if (jsonObj.clefNote) {
note.clefNote = SmoNoteModifierBase.deserialize(jsonObj.clefNote);
}
return note;
}
}
55 changes: 54 additions & 1 deletion src/smo/data/noteModifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
* pitch itself. This includes grace notes, and note-text like lyrics.
* @module /smo/data/noteModifiers
*/
import { SmoAttrs, Ticks, Pitch, getId, SmoObjectParams, Transposable, SvgBox, SmoModifierBase } from './common';
import { SmoAttrs, Ticks, Pitch, getId, SmoObjectParams, Transposable, SvgBox, SmoModifierBase,
Clef, IsClef } from './common';
import { smoSerialize } from '../../common/serializationHelpers';
import { SmoSelector } from '../xform/selections';
import { SmoMusic } from './music';
Expand Down Expand Up @@ -50,6 +51,58 @@ export abstract class SmoNoteModifierBase implements SmoModifierBase {
abstract serialize(): any;
}

export function isClefChangeParamsSer(params: Partial<SmoClefChangeParamsSer>): params is SmoClefChangeParamsSer {
if (typeof(params.clef) === 'string') {
return true;
}
return false;
}
export interface SmoClefChangeParams {
clef: string
}

export interface SmoClefChangeParamsSer extends SmoClefChangeParams {
/**
* constructor
*/
ctor: string;
/**
* attributes for ID
*/
attrs: SmoAttrs;
}

export class SmoClefChange extends SmoNoteModifierBase {
clef: Clef;
static get defaults() {
const rv: SmoClefChangeParamsSer = JSON.parse(JSON.stringify({
clef: 'treble',
ctor: 'SmoClefChange',
attrs: {
id: getId(),
type: 'SmoClefChange'
}
}));
return rv;
}
constructor(clefParams: SmoClefChangeParams) {
super('SmoClefChange');
const clef = clefParams.clef;
if (!IsClef(clef)) {
this.clef = 'treble';
} else {
this.clef = clef as Clef;
}
}
serialize(): SmoClefChangeParamsSer {
const params: Partial<SmoClefChangeParamsSer> = { ctor: 'SmoGraceNote' };
params.clef = this.clef;
if (!isClefChangeParamsSer(params)) {
throw('corrupt clef change');
}
return params;
}
}
/**
* used to construct {@link SmoGraceNote}
* beam group.
Expand Down
6 changes: 3 additions & 3 deletions src/ui/dialogs/arpeggio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class SuiArpeggioAdapter extends SuiComponentAdapter {
* export const SmoArpeggioTypes = ['directionless', 'rasquedo_up', 'rasquedo_down',
'roll_up', 'roll_down', 'brush_up', 'brush_down', 'none'];
*/
export class SuiScoreArpeggioDialog extends SuiDialogAdapterBase<SuiArpeggioAdapter> {
export class SuiArpeggioDialog extends SuiDialogAdapterBase<SuiArpeggioAdapter> {
/**
* The template used to create the dialog components
*/
Expand Down Expand Up @@ -85,12 +85,12 @@ export class SuiScoreArpeggioDialog extends SuiDialogAdapterBase<SuiArpeggioAdap
staticText: []
};
static createAndDisplay(parameters: SuiDialogParams) {
const dg = new SuiScoreArpeggioDialog(parameters);
const dg = new SuiArpeggioDialog(parameters);
dg.display();
}
constructor(params: SuiDialogParams) {
const adapter = new SuiArpeggioAdapter(params.view);
super(SuiScoreArpeggioDialog.dialogElements, { adapter, ...params });
super(SuiArpeggioDialog.dialogElements, { adapter, ...params });
this.modifier = params.modifier;
}
}
Loading

0 comments on commit 5277b3d

Please sign in to comment.