GraphicalStaffEntry.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. import {SourceStaffEntry} from "../VoiceData/SourceStaffEntry";
  2. import {BoundingBox} from "./BoundingBox";
  3. import {Fraction} from "../../Common/DataObjects/Fraction";
  4. import {VerticalGraphicalStaffEntryContainer} from "./VerticalGraphicalStaffEntryContainer";
  5. import {Note} from "../VoiceData/Note";
  6. import {Slur} from "../VoiceData/Expressions/ContinuousExpressions/Slur";
  7. import {Voice} from "../VoiceData/Voice";
  8. import {VoiceEntry} from "../VoiceData/VoiceEntry";
  9. import {GraphicalTie} from "./GraphicalTie";
  10. import {GraphicalObject} from "./GraphicalObject";
  11. import {GraphicalMeasure} from "./GraphicalMeasure";
  12. import {GraphicalNote} from "./GraphicalNote";
  13. import {GraphicalChordSymbolContainer} from "./GraphicalChordSymbolContainer";
  14. import {GraphicalLyricEntry} from "./GraphicalLyricEntry";
  15. import {AbstractGraphicalInstruction} from "./AbstractGraphicalInstruction";
  16. import {GraphicalStaffEntryLink} from "./GraphicalStaffEntryLink";
  17. import {CollectionUtil} from "../../Util/CollectionUtil";
  18. import { GraphicalVoiceEntry } from "./GraphicalVoiceEntry";
  19. import { MusicSheetCalculator } from "./MusicSheetCalculator";
  20. /**
  21. * The graphical counterpart of a [[SourceStaffEntry]].
  22. */
  23. export abstract class GraphicalStaffEntry extends GraphicalObject {
  24. constructor(parentMeasure: GraphicalMeasure, sourceStaffEntry: SourceStaffEntry = undefined, staffEntryParent: GraphicalStaffEntry = undefined) {
  25. super();
  26. this.parentMeasure = parentMeasure;
  27. this.graphicalVoiceEntries = [];
  28. this.sourceStaffEntry = sourceStaffEntry;
  29. if (staffEntryParent !== undefined) {
  30. this.staffEntryParent = staffEntryParent;
  31. this.parentVerticalContainer = staffEntryParent.parentVerticalContainer;
  32. this.PositionAndShape = new BoundingBox(this, staffEntryParent.PositionAndShape);
  33. } else {
  34. this.PositionAndShape = new BoundingBox(this, parentMeasure.PositionAndShape);
  35. }
  36. if (sourceStaffEntry !== undefined) {
  37. this.relInMeasureTimestamp = sourceStaffEntry.Timestamp;
  38. }
  39. }
  40. public graphicalChordContainer: GraphicalChordSymbolContainer;
  41. public graphicalLink: GraphicalStaffEntryLink;
  42. // Extra member needed, as tie notes have no direct source entry with the right time stamp.
  43. public relInMeasureTimestamp: Fraction;
  44. public sourceStaffEntry: SourceStaffEntry;
  45. public parentMeasure: GraphicalMeasure;
  46. public graphicalVoiceEntries: GraphicalVoiceEntry[];
  47. public staffEntryParent: GraphicalStaffEntry;
  48. public parentVerticalContainer: VerticalGraphicalStaffEntryContainer;
  49. private graphicalInstructions: AbstractGraphicalInstruction[] = [];
  50. private graphicalTies: GraphicalTie[] = [];
  51. private lyricsEntries: GraphicalLyricEntry[] = [];
  52. public get GraphicalInstructions(): AbstractGraphicalInstruction[] {
  53. return this.graphicalInstructions;
  54. }
  55. public get GraphicalTies(): GraphicalTie[] {
  56. return this.graphicalTies;
  57. }
  58. public get LyricsEntries(): GraphicalLyricEntry[] {
  59. return this.lyricsEntries;
  60. }
  61. public set LyricsEntries(value: GraphicalLyricEntry[]) {
  62. this.lyricsEntries = value;
  63. }
  64. /**
  65. * Calculate the absolute Timestamp.
  66. * @returns {Fraction}
  67. */
  68. public getAbsoluteTimestamp(): Fraction {
  69. const result: Fraction = this.parentMeasure.parentSourceMeasure.AbsoluteTimestamp.clone();
  70. if (this.relInMeasureTimestamp !== undefined) {
  71. result.Add(this.relInMeasureTimestamp);
  72. }
  73. return result;
  74. }
  75. /**
  76. * Search through all the GraphicalNotes to find the suitable one for a TieEndNote.
  77. * @param tieNote
  78. * @returns {any}
  79. */
  80. public findEndTieGraphicalNoteFromNote(tieNote: Note): GraphicalNote {
  81. for (const gve of this.graphicalVoiceEntries) {
  82. for (const graphicalNote of gve.notes) {
  83. const note: Note = graphicalNote.sourceNote;
  84. if (!note.isRest()
  85. && note.Pitch.FundamentalNote === tieNote.Pitch.FundamentalNote
  86. && note.Pitch.Octave === tieNote.Pitch.Octave
  87. && note.getAbsoluteTimestamp().Equals(tieNote.getAbsoluteTimestamp())) {
  88. return graphicalNote;
  89. }
  90. }
  91. }
  92. return undefined;
  93. }
  94. /**
  95. * Search through all [[GraphicalNote]]s to find the suitable one for an StartSlurNote (that 's also an EndTieNote).
  96. * @param tieNote
  97. * @param slur
  98. * @returns {any}
  99. */
  100. public findEndTieGraphicalNoteFromNoteWithStartingSlur(tieNote: Note, slur: Slur): GraphicalNote {
  101. if (tieNote === undefined) {
  102. return undefined;
  103. }
  104. for (const gve of this.graphicalVoiceEntries) {
  105. if (gve.parentVoiceEntry !== tieNote.ParentVoiceEntry) {
  106. continue;
  107. }
  108. for (const graphicalNote of gve.notes) {
  109. const note: Note = graphicalNote.sourceNote;
  110. if (note.NoteTie !== undefined && note.NoteSlurs.indexOf(slur) !== -1) {
  111. return graphicalNote;
  112. }
  113. }
  114. }
  115. return undefined;
  116. }
  117. public findGraphicalNoteFromGraceNote(graceNote: Note): GraphicalNote {
  118. if (graceNote === undefined) {
  119. return undefined;
  120. }
  121. for (const gve of this.graphicalVoiceEntries) {
  122. if (gve.parentVoiceEntry !== graceNote.ParentVoiceEntry) {
  123. continue;
  124. }
  125. for (const graphicalNote of gve.notes) {
  126. if (graphicalNote.sourceNote === graceNote) {
  127. return graphicalNote;
  128. }
  129. }
  130. }
  131. return undefined;
  132. }
  133. public findGraphicalNoteFromNote(note: Note): GraphicalNote {
  134. if (note === undefined) {
  135. return undefined;
  136. }
  137. for (const gve of this.graphicalVoiceEntries) {
  138. if (gve.parentVoiceEntry !== note.ParentVoiceEntry) {
  139. continue;
  140. }
  141. for (const graphicalNote of gve.notes) {
  142. if (graphicalNote.sourceNote === note && this.getAbsoluteTimestamp().Equals(note.getAbsoluteTimestamp())) {
  143. return graphicalNote;
  144. }
  145. }
  146. }
  147. return undefined;
  148. }
  149. public getGraphicalNoteDurationFromVoice(voice: Voice): Fraction {
  150. for (const gve of this.graphicalVoiceEntries) {
  151. if (gve.parentVoiceEntry.ParentVoice !== voice) {
  152. continue;
  153. }
  154. return gve.notes[0].graphicalNoteLength;
  155. }
  156. return new Fraction(0, 1);
  157. }
  158. /**
  159. * Find the [[StaffEntry]]'s [[GraphicalNote]]s that correspond to the given [[VoiceEntry]]'s [[Note]]s.
  160. * @param voiceEntry
  161. * @returns {any}
  162. */
  163. public findVoiceEntryGraphicalNotes(voiceEntry: VoiceEntry): GraphicalNote[] {
  164. for (const gve of this.graphicalVoiceEntries) {
  165. if (gve.parentVoiceEntry === voiceEntry) {
  166. return gve.notes;
  167. }
  168. }
  169. return undefined;
  170. }
  171. /**
  172. * Check if the given [[VoiceEntry]] is part of the [[StaffEntry]]'s Linked [[VoiceEntry]].
  173. * @param voiceEntry
  174. * @returns {boolean}
  175. */
  176. public isVoiceEntryPartOfLinkedVoiceEntry(voiceEntry: VoiceEntry): boolean {
  177. if (this.sourceStaffEntry.Link !== undefined) {
  178. for (let idx: number = 0, len: number = this.sourceStaffEntry.Link.LinkStaffEntries.length; idx < len; ++idx) {
  179. const sEntry: SourceStaffEntry = this.sourceStaffEntry.Link.LinkStaffEntries[idx];
  180. if (sEntry.VoiceEntries.indexOf(voiceEntry) !== -1 && sEntry !== this.sourceStaffEntry) {
  181. return true;
  182. }
  183. }
  184. }
  185. return false;
  186. }
  187. /**
  188. * Return the [[StaffEntry]]'s Minimum NoteLength.
  189. * @returns {Fraction}
  190. */
  191. public findStaffEntryMinNoteLength(): Fraction {
  192. let minLength: Fraction = new Fraction(Number.MAX_VALUE, 1);
  193. for (const gve of this.graphicalVoiceEntries) {
  194. for (const graphicalNote of gve.notes) {
  195. const calNoteLen: Fraction = graphicalNote.graphicalNoteLength;
  196. if (calNoteLen.lt(minLength) && calNoteLen.GetExpandedNumerator() > 0) {
  197. minLength = calNoteLen;
  198. }
  199. }
  200. }
  201. return minLength;
  202. }
  203. public findStaffEntryMaxNoteLength(): Fraction {
  204. let maxLength: Fraction = new Fraction(0, 1);
  205. for (const gve of this.graphicalVoiceEntries) {
  206. for (const graphicalNote of gve.notes) {
  207. const calNoteLen: Fraction = graphicalNote.graphicalNoteLength;
  208. if (maxLength.lt(calNoteLen) && calNoteLen.GetExpandedNumerator() > 0) {
  209. maxLength = calNoteLen;
  210. }
  211. }
  212. }
  213. return maxLength;
  214. }
  215. /**
  216. * Find or creates the list of [[GraphicalNote]]s in case of a [[VoiceEntry]] (not from TiedNote).
  217. * @param voiceEntry
  218. * @returns {GraphicalNote[]}
  219. */
  220. public findOrCreateGraphicalVoiceEntry(voiceEntry: VoiceEntry): GraphicalVoiceEntry {
  221. for (const gve of this.graphicalVoiceEntries) {
  222. if (gve.parentVoiceEntry === voiceEntry) {
  223. return gve;
  224. }
  225. }
  226. // if not found in list, create new one and add to list:
  227. const graphicalVoiceEntry: GraphicalVoiceEntry = MusicSheetCalculator.symbolFactory.createVoiceEntry(voiceEntry, this);
  228. this.graphicalVoiceEntries.push(graphicalVoiceEntry);
  229. return graphicalVoiceEntry;
  230. }
  231. /**
  232. * Find or creates the list of [[GraphicalNote]]s in case of a TiedNote.
  233. * @param graphicalNote
  234. * @returns {GraphicalNote[]}
  235. */
  236. public findOrCreateGraphicalVoiceEntryFromGraphicalNote(graphicalNote: GraphicalNote): GraphicalVoiceEntry {
  237. for (const gve of this.graphicalVoiceEntries) {
  238. if (gve === graphicalNote.parentVoiceEntry) {
  239. return gve;
  240. }
  241. }
  242. // if not found in list, create new one and add to list:
  243. const graphicalVoiceEntry: GraphicalVoiceEntry = MusicSheetCalculator.symbolFactory.createVoiceEntry(graphicalNote.sourceNote.ParentVoiceEntry, this);
  244. this.graphicalVoiceEntries.push(graphicalVoiceEntry);
  245. return graphicalVoiceEntry;
  246. }
  247. /**
  248. * Insert the [[GraphicalNote]] to the correct index of the [[GraphicalNote]]s list,
  249. * so that the order of the [[GraphicalNote]]'s in the list corresponds to the [[VoiceEntry]]'s [[Note]]s order.
  250. * (needed when adding Tie-EndNotes).
  251. * @param graphicalNotes
  252. * @param graphicalNote
  253. */
  254. public addGraphicalNoteToListAtCorrectYPosition(gve: GraphicalVoiceEntry, graphicalNote: GraphicalNote): void {
  255. const graphicalNotes: GraphicalNote[] = gve.notes;
  256. if (graphicalNotes.length === 0 ||
  257. graphicalNote.PositionAndShape.RelativePosition.y < CollectionUtil.last(graphicalNotes).PositionAndShape.RelativePosition.Y) {
  258. graphicalNotes.push(graphicalNote);
  259. } else {
  260. for (let i: number = graphicalNotes.length - 1; i >= 0; i--) {
  261. if (graphicalNotes[i].PositionAndShape.RelativePosition.y > graphicalNote.PositionAndShape.RelativePosition.y) {
  262. graphicalNotes.splice(i + 1, 0, graphicalNote);
  263. break;
  264. }
  265. if (i === 0) {
  266. graphicalNotes.splice(0, 0, graphicalNote);
  267. break;
  268. }
  269. }
  270. }
  271. }
  272. /**
  273. * Returns true if this staff entry has only rests
  274. */
  275. public hasOnlyRests(): boolean {
  276. const hasOnlyRests: boolean = true;
  277. for (const gve of this.graphicalVoiceEntries) {
  278. for (const graphicalNote of gve.notes) {
  279. const note: Note = graphicalNote.sourceNote;
  280. if (!note.isRest()) {
  281. return false;
  282. }
  283. }
  284. }
  285. return hasOnlyRests;
  286. }
  287. }