GraphicalStaffEntry.ts 12 KB

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