/
VexFlowGraphicalSymbolFactory.ts
239 lines (220 loc) · 11.7 KB
/
VexFlowGraphicalSymbolFactory.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
import Vex from "vexflow";
import VF = Vex.Flow;
import {IGraphicalSymbolFactory} from "../../Interfaces/IGraphicalSymbolFactory";
import {MusicSystem} from "../MusicSystem";
import {VexFlowMusicSystem} from "./VexFlowMusicSystem";
import {Staff} from "../../VoiceData/Staff";
import {StaffLine} from "../StaffLine";
import {SourceMeasure} from "../../VoiceData/SourceMeasure";
import {GraphicalMeasure} from "../GraphicalMeasure";
import {VexFlowMeasure} from "./VexFlowMeasure";
import {SourceStaffEntry} from "../../VoiceData/SourceStaffEntry";
import {GraphicalStaffEntry} from "../GraphicalStaffEntry";
import {VexFlowStaffEntry} from "./VexFlowStaffEntry";
import {Note} from "../../VoiceData/Note";
import {ClefInstruction} from "../../VoiceData/Instructions/ClefInstruction";
import {OctaveEnum} from "../../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
import {GraphicalNote} from "../GraphicalNote";
import {Pitch} from "../../../Common/DataObjects/Pitch";
import {VexFlowGraphicalNote} from "./VexFlowGraphicalNote";
import {Fraction} from "../../../Common/DataObjects/Fraction";
import {GraphicalChordSymbolContainer} from "../GraphicalChordSymbolContainer";
import {GraphicalLabel} from "../GraphicalLabel";
import {EngravingRules} from "../EngravingRules";
import { TechnicalInstruction } from "../../VoiceData/Instructions/TechnicalInstruction";
import { GraphicalVoiceEntry } from "../GraphicalVoiceEntry";
import { VoiceEntry } from "../../VoiceData/VoiceEntry";
import { VexFlowVoiceEntry } from "./VexFlowVoiceEntry";
import { VexFlowConverter } from "./VexFlowConverter";
import { VexFlowTabMeasure } from "./VexFlowTabMeasure";
import { VexFlowStaffLine } from "./VexFlowStaffLine";
import { KeyInstruction } from "../../VoiceData/Instructions/KeyInstruction";
import { VexFlowMultiRestMeasure } from "./VexFlowMultiRestMeasure";
import { BoundingBox } from "../BoundingBox";
export class VexFlowGraphicalSymbolFactory implements IGraphicalSymbolFactory {
/**
* Create a new music system for the given page.
* Currently only one vertically endless page exists where all systems are put to.
* @param page
* @param systemIndex
* @returns {VexFlowMusicSystem}
*/
public createMusicSystem(systemIndex: number, rules: EngravingRules): MusicSystem {
return new VexFlowMusicSystem(systemIndex, rules);
}
/**
* Create a staffline object containing all staff measures belonging to a given system and staff.
* @param parentSystem
* @param parentStaff
* @returns {VexFlowStaffLine}
*/
public createStaffLine(parentSystem: MusicSystem, parentStaff: Staff): StaffLine {
return new VexFlowStaffLine(parentSystem, parentStaff);
}
/**
* Construct an empty graphicalMeasure from the given source measure and staff.
* @param sourceMeasure
* @param staff
* @returns {VexFlowMeasure}
*/
public createGraphicalMeasure(sourceMeasure: SourceMeasure, staff: Staff, isTabMeasure: boolean = false): GraphicalMeasure {
return new VexFlowMeasure(staff, sourceMeasure, undefined);
}
/**
* Construct a MultiRestMeasure from the given source measure and staff.
* @param sourceMeasure
* @param staff
* @returns {VexFlowMultiRestMeasure}
*/
public createMultiRestMeasure(sourceMeasure: SourceMeasure, staff: Staff, staffLine?: StaffLine): GraphicalMeasure {
return new VexFlowMultiRestMeasure(staff, sourceMeasure, staffLine);
}
/**
* Construct an empty Tab staffMeasure from the given source measure and staff.
* @param sourceMeasure
* @param staff
* @returns {VexFlowTabMeasure}
*/
public createTabStaffMeasure(sourceMeasure: SourceMeasure, staff: Staff): GraphicalMeasure {
return new VexFlowTabMeasure(staff, sourceMeasure);
}
/**
* Create empty measure, which will be used to show key, rhythm changes at the end of the system.
* @param staffLine
* @returns {VexFlowMeasure}
*/
public createExtraGraphicalMeasure(staffLine: StaffLine): GraphicalMeasure {
const extraGraphicalMeasure: GraphicalMeasure = new VexFlowMeasure(staffLine.ParentStaff, undefined, staffLine);
extraGraphicalMeasure.IsExtraGraphicalMeasure = true; // this also means that MeasureNumber < 0 because unchanged
extraGraphicalMeasure.ExtraGraphicalMeasurePreviousMeasure = staffLine.Measures.last();
return extraGraphicalMeasure;
}
/**
* Create a staffEntry in the given measure for a given sourceStaffEntry.
* @param sourceStaffEntry
* @param measure
* @returns {VexFlowStaffEntry}
*/
public createStaffEntry(sourceStaffEntry: SourceStaffEntry, measure: GraphicalMeasure): GraphicalStaffEntry {
return new VexFlowStaffEntry(<VexFlowMeasure>measure, sourceStaffEntry, undefined);
}
public createVoiceEntry(parentVoiceEntry: VoiceEntry, parentStaffEntry: GraphicalStaffEntry): GraphicalVoiceEntry {
return new VexFlowVoiceEntry(parentVoiceEntry, parentStaffEntry);
}
/**
* Create a Graphical Note for given note and clef and as part of graphicalStaffEntry.
* @param note
* @param numberOfDots The number of dots the note has to increase its musical duration.
* @param graphicalStaffEntry
* @param activeClef The currently active clef, needed for positioning the note vertically
* @param octaveShift The currently active octave transposition enum, needed for positioning the note vertically
* @returns {GraphicalNote}
*/
public createNote(note: Note, graphicalVoiceEntry: GraphicalVoiceEntry, activeClef: ClefInstruction,
octaveShift: OctaveEnum = OctaveEnum.NONE, rules: EngravingRules,
graphicalNoteLength: Fraction = undefined): GraphicalNote {
return new VexFlowGraphicalNote(note, graphicalVoiceEntry, activeClef, octaveShift, rules, graphicalNoteLength);
}
/**
* Create a Graphical Grace Note (smaller head, stem...) for given note and clef and as part of graphicalStaffEntry.
* @param note
* @param numberOfDots
* @param graphicalVoiceEntry
* @param activeClef
* @param octaveShift
* @returns {GraphicalNote}
*/
public createGraceNote(note: Note, graphicalVoiceEntry: GraphicalVoiceEntry,
activeClef: ClefInstruction, rules: EngravingRules,
octaveShift: OctaveEnum = OctaveEnum.NONE): GraphicalNote {
return new VexFlowGraphicalNote(note, graphicalVoiceEntry, activeClef, octaveShift, rules);
}
/**
* Sets a pitch which will be used for rendering the given graphical note (not changing the original pitch of the note!!!).
* Will be only called if the displayed accidental is different from the original (e.g. a C# with C# as key instruction)
* @param graphicalNote
* @param pitch The pitch which will be rendered.
*/
public addGraphicalAccidental(graphicalNote: GraphicalNote, pitch: Pitch): void {
const note: VexFlowGraphicalNote = (graphicalNote as VexFlowGraphicalNote);
// accidental is added in setPitch
note.setAccidental(pitch);
}
/**
* Adds a Fermata symbol at the last note of the given tied Note.
* The last graphical note of this tied note is located at the given graphicalStaffEntry.
* A Fermata has to be located at the last tied note.
* @param tiedNote
* @param graphicalStaffEntry
*/
public addFermataAtTiedEndNote(tiedNote: Note, graphicalStaffEntry: GraphicalStaffEntry): void {
return;
}
/**
* Adds a clef change within a measure before the given staff entry.
* @param graphicalStaffEntry
* @param clefInstruction
*/
public createInStaffClef(graphicalStaffEntry: GraphicalStaffEntry, clefInstruction: ClefInstruction): void {
const se: VexFlowStaffEntry = graphicalStaffEntry as VexFlowStaffEntry;
const vfClefParams: { type: string, size: string, annotation: string } = VexFlowConverter.Clef(clefInstruction, "small");
se.vfClefBefore = new VF.ClefNote(vfClefParams.type, vfClefParams.size, vfClefParams.annotation);
return;
}
/**
* Adds a chord symbol at the given staff entry
* @param sourceStaffEntry
* @param graphicalStaffEntry
* @param transposeHalftones
*/
public createChordSymbols( sourceStaffEntry: SourceStaffEntry,
graphicalStaffEntry: GraphicalStaffEntry,
keyInstruction: KeyInstruction,
transposeHalftones: number): void {
const rules: EngravingRules = graphicalStaffEntry.parentMeasure.parentSourceMeasure.Rules;
let xShift: number = 0;
const chordSymbolSpacing: number = rules.ChordSymbolXSpacing;
for (const chordSymbolContainer of sourceStaffEntry.ChordContainers) {
let parentBbox: BoundingBox = graphicalStaffEntry.PositionAndShape;
if (graphicalStaffEntry.graphicalVoiceEntries.length === 1 &&
graphicalStaffEntry.graphicalVoiceEntries[0].notes.length === 1 &&
graphicalStaffEntry.graphicalVoiceEntries[0].notes[0].sourceNote.isWholeRest()) {
// graphicalStaffEntry.graphicalVoiceEntries[0]?.notes[0]?.sourceNote.IsWholeMeasureRest // not yet set apparently
// whole measure rests: position at start of measure instead of middle like the rest note
// xShift -= graphicalStaffEntry.PositionAndShape.RelativePosition.x; // unfortunately relative x is 0 here
parentBbox = graphicalStaffEntry.parentMeasure.PositionAndShape;
xShift += graphicalStaffEntry.parentMeasure.beginInstructionsWidth;
xShift += rules.ChordSymbolWholeMeasureRestXOffset; // margin to start of measure / bar
}
const graphicalChordSymbolContainer: GraphicalChordSymbolContainer =
new GraphicalChordSymbolContainer(chordSymbolContainer,
parentBbox,
rules.ChordSymbolTextHeight,
keyInstruction,
transposeHalftones,
graphicalStaffEntry.parentMeasure.parentSourceMeasure.Rules // TODO undefined sometimes
);
const graphicalLabel: GraphicalLabel = graphicalChordSymbolContainer.GraphicalLabel;
graphicalLabel.PositionAndShape.RelativePosition.y -= rules.ChordSymbolYOffset;
graphicalLabel.setLabelPositionAndShapeBorders(); // to get Size.width
let extraXShiftForShortChordSymbols: number = 0;
if (graphicalLabel.PositionAndShape.Size.width < rules.ChordSymbolExtraXShiftWidthThreshold) {
extraXShiftForShortChordSymbols = rules.ChordSymbolExtraXShiftForShortChordSymbols;
}
graphicalLabel.PositionAndShape.RelativePosition.x += xShift + extraXShiftForShortChordSymbols;
graphicalLabel.setLabelPositionAndShapeBorders();
// TODO check for available space until next staffEntry or chord symbol? (x direction)
graphicalChordSymbolContainer.PositionAndShape.calculateBoundingBox();
graphicalStaffEntry.graphicalChordContainers.push(graphicalChordSymbolContainer);
xShift += graphicalLabel.PositionAndShape.Size.width + chordSymbolSpacing;
}
}
/**
* Adds a technical instruction at the given staff entry.
* @param technicalInstruction
* @param graphicalStaffEntry
*/
public createGraphicalTechnicalInstruction(technicalInstruction: TechnicalInstruction, graphicalStaffEntry: GraphicalStaffEntry): void {
return;
}
}