Source

vendors/P5/sketchPerformance.js

  1. // File imports
  2. import atVars from "../AlphaTab/variables";
  3. import * as sketchBehaviors from "./sketchBehaviors";
  4. /**
  5. * P5 sketch wrapper for performance highlighting
  6. * @module sketchPerformance
  7. * @category P5
  8. * @author Daniel Griessler <dgriessler20@gmail.com>
  9. * @author Dan Levy <danlevy124@gmail.com>
  10. */
  11. /**
  12. * Wrapper for local p5 setup and draw functions
  13. * @function
  14. * @param {sketch} p Sketch object that will include all of the functions that will be called by p5
  15. */
  16. const p5PerformanceSketch = (p) => {
  17. p.type = sketchBehaviors.PERFORMANCE_HIGHLIGHTING;
  18. // document elements retrieved from the document
  19. let barCursor;
  20. let alphaTabSurface;
  21. // provided by reference which is updated in other functions
  22. let drawer;
  23. // reference to p5's canvas when created. It should overlay the AlphaTab canvas
  24. let canvas;
  25. const STATE_DEFAULT = 1;
  26. const STATE_HIGHLIGHT = 2;
  27. let state = STATE_DEFAULT;
  28. let latestDrawnMeasure = -1;
  29. let latestBase = 0;
  30. let musicSections = [];
  31. let currentMusicSection = null;
  32. /**
  33. * This function is called twice. Once, upon initialization p5 calls it which we use to tell p5 to stop looping
  34. * Then, AlphaTab will call setup when its done being rendered. Then, the canvas can be setup for drawing since
  35. * the canvas overlays the AlphaTab container
  36. * @param {Drawer} drawerGiven - p5 will not provide this but atVars provides a reference to the Drawer being used
  37. */
  38. p.setup = function (drawerGiven) {
  39. if (drawerGiven === undefined) {
  40. p.noLoop();
  41. return;
  42. }
  43. drawer = drawerGiven;
  44. // Retrieved by attaching unique IDs to elements generated by AlphaTab. Required editing AlphaTab.js directly
  45. barCursor = document.getElementById("bC");
  46. alphaTabSurface = document.getElementById("aTS");
  47. // creates a canvas that overlaps the alphaTabSurface. Position is absolute for the canvas by default
  48. canvas = p.createCanvas(
  49. alphaTabSurface.clientWidth,
  50. alphaTabSurface.clientHeight
  51. );
  52. const x = 0;
  53. const y = 0;
  54. canvas.position(x, y);
  55. canvas.parent("sketch-holder");
  56. p.loop();
  57. };
  58. const getPositionsObj = function (obj) {
  59. return {
  60. left: parseInt(obj.left.substring(0, obj.left.length - 2), 10),
  61. top: parseInt(obj.top.substring(0, obj.top.length - 2), 10),
  62. width: parseInt(obj.width.substring(0, obj.width.length - 2), 10),
  63. height: parseInt(
  64. obj.height.substring(0, obj.height.length - 2),
  65. 10
  66. ),
  67. };
  68. };
  69. const setDrawColor = (measureNumber) => {
  70. let measureIndex = measureNumber - 1;
  71. if (
  72. atVars.texLoaded &&
  73. measureIndex < atVars.texLoaded.performanceProgress.length &&
  74. measureIndex >= 0
  75. ) {
  76. let score = atVars.texLoaded.performanceProgress[measureIndex];
  77. if (score > 90) {
  78. p.fill(0, 255, 0);
  79. } else if (score > 80) {
  80. p.fill(255, 255, 0);
  81. } else {
  82. p.fill(255, 0, 0);
  83. }
  84. } else {
  85. p.fill(255, 0, 0);
  86. }
  87. };
  88. /**
  89. * Draws the canvas on the screen. Requires that the canvas is not undefined ie setup has run
  90. * TODO: Handle sheet music scale
  91. */
  92. p.draw = function () {
  93. if (!atVars.getsFeedback || !drawer) {
  94. return;
  95. }
  96. // TODO: Fix the first measure highlighting
  97. if (
  98. atVars &&
  99. atVars.sketchBehavior === sketchBehaviors.PERFORMANCE_HIGHLIGHTING
  100. ) {
  101. let firstBarPos = atVars.texLoaded.firstBarMeasurePosition;
  102. let compareBarPos = null;
  103. try {
  104. let cursorBarStyle = document.getElementsByClassName(
  105. "at-cursor-bar"
  106. )[0].style;
  107. compareBarPos = getPositionsObj(cursorBarStyle);
  108. } catch (error) {}
  109. if (compareBarPos === null || firstBarPos === null) {
  110. return;
  111. }
  112. if (state === STATE_DEFAULT) {
  113. state = STATE_HIGHLIGHT;
  114. p.clear();
  115. }
  116. const measurePositions = document
  117. .getElementById("aTS")
  118. .getElementsByClassName("measureSeparator");
  119. p.noStroke();
  120. if (
  121. !isNaN(compareBarPos.left) &&
  122. !isNaN(compareBarPos.top) &&
  123. !isNaN(compareBarPos.width) &&
  124. !isNaN(compareBarPos.height) &&
  125. (firstBarPos.left !== compareBarPos.left ||
  126. firstBarPos.top !== compareBarPos.top ||
  127. firstBarPos.width !== compareBarPos.width ||
  128. firstBarPos.height !== compareBarPos.height)
  129. ) {
  130. p.fill("#F8F8F8");
  131. // draws clearing rectangle with total height of alpha tab from previous X position to the end
  132. p.rect(
  133. 0,
  134. measurePositions[0].y.baseVal.value,
  135. measurePositions[0].x.baseVal.value,
  136. drawer.distanceBetweenLines * 4
  137. );
  138. atVars.texLoaded.firstBarMeasurePosition = {
  139. left: compareBarPos.left,
  140. top: compareBarPos.top,
  141. width: compareBarPos.width,
  142. height: compareBarPos.height,
  143. };
  144. setDrawColor(1);
  145. let firstBarPos = atVars.texLoaded.firstBarMeasurePosition;
  146. let pos1X = firstBarPos.left;
  147. let pos1Y = firstBarPos.top + drawer.distanceBetweenLines;
  148. let pos2X = measurePositions[0].x.baseVal.value;
  149. p.rect(
  150. pos1X,
  151. pos1Y,
  152. pos2X - pos1X,
  153. drawer.distanceBetweenLines * 4
  154. );
  155. }
  156. if (latestDrawnMeasure === -1) {
  157. setDrawColor(1);
  158. // draws highlight on first measure
  159. let firstBarPos = atVars.texLoaded.firstBarMeasurePosition;
  160. let pos1X = firstBarPos.left;
  161. let pos1Y = firstBarPos.top + drawer.distanceBetweenLines;
  162. let pos2X = measurePositions[0].x.baseVal.value;
  163. p.rect(
  164. pos1X,
  165. pos1Y,
  166. pos2X - pos1X,
  167. drawer.distanceBetweenLines * 4
  168. );
  169. if (!isNaN(pos1X)) {
  170. latestDrawnMeasure++;
  171. currentMusicSection = {
  172. startMeasure: 1,
  173. endMeasure: 1,
  174. base: 0,
  175. };
  176. } else {
  177. atVars.api.timePosition = 0;
  178. atVars.texLoaded.firstBarMeasurePosition = getPositionsObj(
  179. barCursor.style
  180. );
  181. }
  182. } else {
  183. // draws height of later measures only drawing new measures
  184. while (latestDrawnMeasure < measurePositions.length - 1) {
  185. let pos1X =
  186. measurePositions[latestDrawnMeasure].x.baseVal.value +
  187. latestBase;
  188. let pos1Y =
  189. measurePositions[latestDrawnMeasure].y.baseVal.value;
  190. if (
  191. latestDrawnMeasure === 0 ||
  192. pos1Y ===
  193. measurePositions[latestDrawnMeasure - 1].y.baseVal
  194. .value
  195. ) {
  196. latestDrawnMeasure++;
  197. if (
  198. !measurePositions[
  199. latestDrawnMeasure - 1
  200. ].parentNode.isSameNode(
  201. measurePositions[latestDrawnMeasure].parentNode
  202. )
  203. ) {
  204. currentMusicSection.endMeasure = latestDrawnMeasure;
  205. const newSection = JSON.parse(
  206. JSON.stringify(currentMusicSection)
  207. );
  208. musicSections.push(newSection);
  209. latestBase =
  210. latestBase +
  211. measurePositions[latestDrawnMeasure - 1].x
  212. .baseVal.value +
  213. 1;
  214. currentMusicSection.startMeasure = latestDrawnMeasure;
  215. currentMusicSection.base = latestBase;
  216. }
  217. let dist = Math.abs(
  218. measurePositions[latestDrawnMeasure].x.baseVal
  219. .value +
  220. latestBase -
  221. pos1X
  222. );
  223. setDrawColor(latestDrawnMeasure + 1);
  224. p.rect(
  225. pos1X,
  226. pos1Y,
  227. dist,
  228. drawer.distanceBetweenLines * 4
  229. );
  230. } else {
  231. break;
  232. }
  233. }
  234. }
  235. } else if (state === STATE_HIGHLIGHT) {
  236. p.clear();
  237. latestDrawnMeasure = -1;
  238. latestBase = 0;
  239. musicSections.length = 0;
  240. currentMusicSection = null;
  241. state = STATE_DEFAULT;
  242. }
  243. };
  244. p.clear = function () {
  245. latestDrawnMeasure = -1;
  246. latestBase = 0;
  247. musicSections.length = 0;
  248. currentMusicSection = null;
  249. };
  250. };
  251. export default p5PerformanceSketch;