beat-tick.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import { reactive } from 'vue';
  2. import tockAndTick from './tockAndTick.json';
  3. import { Howl } from 'howler';
  4. import { initSelectScorePart, setting } from './setting';
  5. import { beatDesc } from './beat-desc';
  6. const beatData = reactive({
  7. list: [] as number[],
  8. len: 0,
  9. tickEnd: false,
  10. /** 节拍器时间 */
  11. beatLengthInMilliseconds: 0,
  12. loopTime: 0, // 循环时长
  13. state: '',
  14. source1: '' as any,
  15. source2: new Howl({
  16. src: tockAndTick.tock
  17. }) as any,
  18. index: 0,
  19. show: false
  20. });
  21. const handlePlay = (i: number, source: any, timer: any) => {
  22. let payBeatTime = new Date().getTime();
  23. return new Promise(resolve => {
  24. if (beatData.tickEnd) {
  25. resolve(i);
  26. return;
  27. }
  28. let timeSppedEnum = 16.7;
  29. const proofTime = () => {
  30. if (setting.playState !== 'play') {
  31. return;
  32. }
  33. setTimeout(() => {
  34. const currentTime = new Date().getTime();
  35. // 两次定时任务时间间隔
  36. const diffTime = currentTime - payBeatTime;
  37. if (diffTime >= beatData.loopTime) {
  38. beatData.index++;
  39. if (timer.type === 'sound') {
  40. if (source) source.play();
  41. }
  42. resolve(i);
  43. payBeatTime = currentTime;
  44. } else {
  45. if (Math.abs(diffTime - beatData.loopTime) <= timeSppedEnum) {
  46. // 为了处理最后循环时间,用循环耗时
  47. for (let index = 0; index < 500000; index++) {
  48. let forTime = new Date().getTime();
  49. if (Math.abs(forTime - payBeatTime) >= beatData.loopTime) {
  50. beatData.index++;
  51. if (timer.type === 'sound') {
  52. if (source) source.play();
  53. }
  54. resolve(i);
  55. payBeatTime = forTime;
  56. break;
  57. }
  58. }
  59. } else {
  60. proofTime();
  61. }
  62. }
  63. }, timeSppedEnum);
  64. };
  65. proofTime();
  66. });
  67. };
  68. /** 开始节拍器 */
  69. export const handleStartBeat = async () => {
  70. beatData.show = true;
  71. beatData.tickEnd = false;
  72. beatData.index = 0;
  73. beatData.beatLengthInMilliseconds = (60 / setting.speed) * 1000;
  74. // let startTime = +new Date();
  75. for (let i = 0; i < setting.scorePart.length; i++) {
  76. if (beatData.tickEnd) return false;
  77. for (let j = 0; j < setting.scorePart[i].length; j++) {
  78. if (beatData.tickEnd) return false;
  79. // 提前结束, 直接放回false
  80. const part = setting.scorePart[i][j];
  81. const params = {
  82. ...part,
  83. ...beatDesc[part.index + 1]
  84. };
  85. const single16th = beatData.beatLengthInMilliseconds;
  86. const source = beatData.source2;
  87. for (let g = 0; g < params.attribute.length; g++) {
  88. let time = 0;
  89. const attr = params.attribute[g];
  90. // 计算每一拍需要的时长
  91. // 四分音符 有延音
  92. switch (attr.number) {
  93. case 4:
  94. if (attr.point) {
  95. time = single16th * 0.5 * 3;
  96. } else {
  97. time = single16th;
  98. }
  99. break;
  100. case 8:
  101. // 连音
  102. if (params.liaison) {
  103. time = single16th * (1 / params.beatNum);
  104. } else if (attr.point) {
  105. time = single16th * 0.25 * 3;
  106. } else {
  107. time = single16th * 0.5;
  108. }
  109. break;
  110. case 16:
  111. if (params.liaison) {
  112. time = single16th * (1 / params.beatNum);
  113. } else {
  114. time = single16th * 0.25;
  115. }
  116. break;
  117. }
  118. await handlePlay(i, source, {
  119. time,
  120. type: attr.type
  121. });
  122. beatData.loopTime = time;
  123. initSelectScorePart(i, j);
  124. }
  125. }
  126. }
  127. // console.log(+new Date() - startTime);
  128. beatData.show = false;
  129. handleStartBeat();
  130. return true;
  131. };
  132. /** 设置节拍器
  133. * @param beatLengthInMilliseconds 节拍间隔时间
  134. * @param beat 节拍数
  135. */
  136. export const handleInitBeat = (
  137. beatLengthInMilliseconds: number,
  138. beat: number
  139. ) => {
  140. beatData.state = '';
  141. beatData.beatLengthInMilliseconds = beatLengthInMilliseconds;
  142. beatData.loopTime = beatLengthInMilliseconds;
  143. beatData.len = beat;
  144. };
  145. /** 节拍器暂停 */
  146. export const hendleEndBeat = () => {
  147. beatData.tickEnd = true;
  148. initSelectScorePart();
  149. };