|
@@ -0,0 +1,477 @@
|
|
|
+import { reactive, watch } from "vue";
|
|
|
+import { tickUrl as tick, tockUrl as tock } from "/src/constant/audios";
|
|
|
+import { browser } from "./utils";
|
|
|
+import state from "../pages/detail/state";
|
|
|
+type IOptions = {
|
|
|
+ speed: number;
|
|
|
+};
|
|
|
+const ac = window.AudioContext || (window as any).webkitAudioContext || (window as any).mozAudioContext || (window as any).msAudioContext;
|
|
|
+const browserInfo = browser();
|
|
|
+let tipsTimer: any = null; // 光标提示定时器
|
|
|
+export const metronomeData = reactive({
|
|
|
+ disable: true,
|
|
|
+ lineShow: false,
|
|
|
+ isClick: false,
|
|
|
+ metro: null as unknown as Metronome,
|
|
|
+ metroList: [] as number[],
|
|
|
+ activeList: [] as number[],
|
|
|
+ metroMeasure: [] as any[],
|
|
|
+ activeIndex: null as unknown as number,
|
|
|
+ activeMetro: {} as any,
|
|
|
+ cursorMode: 1 as number, // 光标模式:1:音符指针;2:节拍指针;3:关闭指针
|
|
|
+ cursorTips: '' as string, // 光标模式提示文字
|
|
|
+});
|
|
|
+
|
|
|
+watch(
|
|
|
+ () => metronomeData.cursorMode,
|
|
|
+ () => {
|
|
|
+ const img: HTMLElement = document.querySelector("#cursorImg-0")!;
|
|
|
+ if (img) {
|
|
|
+ switch (metronomeData.cursorMode) {
|
|
|
+ case 1:
|
|
|
+ img.classList.remove("lineHide");
|
|
|
+ img.style.opacity = 'inherit'
|
|
|
+ metronomeData.cursorTips = '您已切换到指针跟随音符播放';
|
|
|
+ img.style.opacity = 'inherit'
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ img.classList.add("lineHide");
|
|
|
+ img.style.opacity = 'inherit'
|
|
|
+ metronomeData.cursorTips = '您已切换到指针跟随节拍播放';
|
|
|
+ console.log('光标',img)
|
|
|
+ break;
|
|
|
+ case 3:
|
|
|
+ img.style.opacity = '0'
|
|
|
+ metronomeData.cursorTips = '您已关闭指针显示';
|
|
|
+ console.log('隐藏光标')
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ hideCursorTip()
|
|
|
+ }
|
|
|
+ }
|
|
|
+);
|
|
|
+class Metronome {
|
|
|
+ ctx = new ac();
|
|
|
+ playType = "tick";
|
|
|
+ source = null as any; // 创建音频源头
|
|
|
+ source1 = null as any;
|
|
|
+ source2 = null as any;
|
|
|
+
|
|
|
+ constructor(option?: IOptions) {}
|
|
|
+ init(times: any[]) {
|
|
|
+ this.calculation(times);
|
|
|
+ metronomeData.activeList = [];
|
|
|
+ return new Promise(async (resolve) => {
|
|
|
+ if (this.source1 && this.source2) return resolve(true);
|
|
|
+ this.source1 = await this.loadAudio1();
|
|
|
+ this.source2 = await this.loadAudio2();
|
|
|
+ resolve(true);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 播放
|
|
|
+ sound = (currentTime: number) => {
|
|
|
+ // console.log("🚀 ~ currentTime", currentTime)
|
|
|
+ // currentTime = setCurrentTime(currentTime);
|
|
|
+ let index = -1;
|
|
|
+ let activeMetro = -1;
|
|
|
+ for (let i = 0; i < metronomeData.metroList.length; i++) {
|
|
|
+ const item = metronomeData.metroList[i];
|
|
|
+
|
|
|
+ if (currentTime >= item) {
|
|
|
+ // console.log(currentTime , item)
|
|
|
+ index = i;
|
|
|
+ activeMetro = item;
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (index > -1 && metronomeData.activeIndex !== index) {
|
|
|
+ metronomeData.activeIndex = index;
|
|
|
+ // console.log("播放", metronomeData.activeIndex);
|
|
|
+ metronomeData.activeMetro = this.getStep(activeMetro);
|
|
|
+ // console.log("🚀 ~ 节拍metronomeData.activeMetro",metronomeData.activeMetro.measureNumberIndex, metronomeData.activeMetro.index, metronomeData.activeMetro)
|
|
|
+ this.playAudio();
|
|
|
+ metronomeData.isClick = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ metronomeData.isClick = false;
|
|
|
+ };
|
|
|
+ // 播放
|
|
|
+ playAudio = () => {
|
|
|
+ this.source = this.ctx.createBufferSource();
|
|
|
+ this.source.buffer = metronomeData.activeMetro?.index === 0 ? this.source1 : this.source2;
|
|
|
+ const gainNode = this.ctx.createGain();
|
|
|
+ gainNode.gain.value = metronomeData.disable ? 0 : 0.4;
|
|
|
+ this.source.connect(gainNode);
|
|
|
+ gainNode.connect(this.ctx.destination);
|
|
|
+ this.source.start(0); //立即播放
|
|
|
+ };
|
|
|
+
|
|
|
+ // 切换
|
|
|
+ selectPlay() {}
|
|
|
+
|
|
|
+ loadAudio1 = async () => {
|
|
|
+ const audioUrl = tick; // "/tick.wav";
|
|
|
+ const res = await fetch(audioUrl);
|
|
|
+ const arrayBuffer = await res.arrayBuffer(); // byte array字节数组
|
|
|
+ // console.log("🚀 ~ arrayBuffer", arrayBuffer)
|
|
|
+ const audioBuffer = await this.ctx.decodeAudioData(arrayBuffer, function (decodeData) {
|
|
|
+ return decodeData;
|
|
|
+ });
|
|
|
+ return audioBuffer;
|
|
|
+ };
|
|
|
+ loadAudio2 = async () => {
|
|
|
+ const audioUrl = tock; //"/tock.wav";
|
|
|
+ const res = await fetch(audioUrl);
|
|
|
+ const arrayBuffer = await res.arrayBuffer(); // byte array字节数组
|
|
|
+ const audioBuffer = await this.ctx.decodeAudioData(arrayBuffer, function (decodeData) {
|
|
|
+ return decodeData;
|
|
|
+ });
|
|
|
+ return audioBuffer;
|
|
|
+ };
|
|
|
+ getStep(time: number) {
|
|
|
+ for (let i = 0; i < metronomeData.metroMeasure.length; i++) {
|
|
|
+ const list = metronomeData.metroMeasure[i];
|
|
|
+ const item = list.find((n: any) => n.time === time);
|
|
|
+ if (item) {
|
|
|
+ // console.log('index',item)
|
|
|
+ return item;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return {};
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算 所有的拍子的时间
|
|
|
+ calculation(times: any[]) {
|
|
|
+ console.log("🚀 ~ times", times);
|
|
|
+ // 1.统计有多少小节
|
|
|
+ const measures: any[] = [];
|
|
|
+ let xmlNumber = -1;
|
|
|
+ for (let i = 0; i < times.length; i++) {
|
|
|
+ const note = times[i];
|
|
|
+ const measureNumberXML = note?.noteElement?.sourceMeasure?.measureNumber + 1 || -1;
|
|
|
+ // console.log("🚀 ~ note?.noteElement?.sourceMeasure", note?.noteElement?.sourceMeasure)
|
|
|
+ // console.log("🚀 ~ measureNumberXML", measureNumberXML, note)
|
|
|
+ // console.log("🚀 ~ measureNumberXML", note)
|
|
|
+ const measureListIndex = note?.noteElement?.sourceMeasure?.measureListIndex;
|
|
|
+ if (measureNumberXML > -1) {
|
|
|
+ if (measureNumberXML != xmlNumber) {
|
|
|
+ const m = {
|
|
|
+ measureNumberXML: measureNumberXML,
|
|
|
+ measureNumberIndex: measureListIndex,
|
|
|
+ numerator: note?.noteElement?.sourceMeasure?.ActiveTimeSignature?.numerator || 0,
|
|
|
+ start: note.measures[0].time,
|
|
|
+ end: note.measures[note.measures.length - 1].endtime,
|
|
|
+ time: note.measures[note.measures.length - 1].endtime - note.measures[0].time,
|
|
|
+ stave_x: note?.noteElement?.sourceMeasure?.verticalMeasureList?.[0]?.stave?.x || 0,
|
|
|
+ end_x: (note?.stave?.end_x || 0) || 0,
|
|
|
+ stepList: [] as number[],
|
|
|
+ svgs: [] as any[],
|
|
|
+ };
|
|
|
+ // 2.统计小节的拍数
|
|
|
+ // 3.统计小节的时长, 开始时间,结束时间
|
|
|
+ // console.log(measureNumberXML,note.measures, times.filter((n: any) => n?.noteElement?.sourceMeasure?.measureListIndex == measureListIndex))
|
|
|
+ if ([121].includes(state.subjectId)) {
|
|
|
+ const _measures = times.filter((n: any) => n?.noteElement?.sourceMeasure?.measureListIndex == measureListIndex);
|
|
|
+ note.measures = _measures;
|
|
|
+ m.start = note.measures[0].time;
|
|
|
+ m.end = note.measures[note.measures.length - 1].endtime;
|
|
|
+ m.time = note.measures[note.measures.length - 1].endtime - note.measures[0].time;
|
|
|
+ try {
|
|
|
+ const tickables = note.noteElement.sourceMeasure.verticalMeasureList.reduce((arr: any[], value: any) => {
|
|
|
+ arr.push(...value.vfVoices["1"].tickables);
|
|
|
+ return arr;
|
|
|
+ }, []);
|
|
|
+ const xList: any[] = [];
|
|
|
+ m.svgs = tickables
|
|
|
+ .map((n: any) => {
|
|
|
+ const x = n.getBoundingBox().x;
|
|
|
+ if (!xList.includes(x) && n.duration !== "w") {
|
|
|
+ xList.push(x);
|
|
|
+ n._start_x = x;
|
|
|
+ return n;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .filter(Boolean)
|
|
|
+ .sort((a: any, b: any) => a._start_x - b._start_x);
|
|
|
+ // console.log(measureNumberXML, m.svgs)
|
|
|
+ } catch (error) {
|
|
|
+ console.log(error);
|
|
|
+ }
|
|
|
+ m.stepList = calculateMutilpleMetroStep(note.measures, m);
|
|
|
+ } else {
|
|
|
+ m.stepList = calculateMetroStep(note.measures, m);
|
|
|
+ }
|
|
|
+ measures.push(m);
|
|
|
+ xmlNumber = measureNumberXML;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // console.log(measures, measures.length);
|
|
|
+
|
|
|
+ let metroList: number[] = [];
|
|
|
+ const metroMeasure: any[] = [];
|
|
|
+ // 4.按照拍数将时长平均分配
|
|
|
+ try {
|
|
|
+ for (let i = 0; i < measures.length; i++) {
|
|
|
+ const measure = measures[i];
|
|
|
+ const noteStep = measure.time / measure.numerator;
|
|
|
+ // console.log("🚀 ~ measure.measureNumberXML",measure.measureNumberXML, noteStep)
|
|
|
+ const WIDTH = [121].includes(state.subjectId) ? 95 : 100;
|
|
|
+ const widthStep = WIDTH / (measure.numerator + 1);
|
|
|
+ metroMeasure[i] = [] as number[];
|
|
|
+ // console.log('stepList', [...measure.stepList], measure.measureNumberXML)
|
|
|
+ for (let j = 0; j < measure.numerator; j++) {
|
|
|
+ const time = noteStep * j + measure.start;
|
|
|
+ metroList.push(time);
|
|
|
+ let left = "";
|
|
|
+ if (measure.stepList[j] === -1 || (measure.measureNumberXML === 1 && !measure.stepList[j])) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if (measure.stepList[j]) {
|
|
|
+ left = measure.stepList[j] + "px";
|
|
|
+ } else {
|
|
|
+ const preLeft = measure.stepList[j - 1];
|
|
|
+ left = !preLeft ? `${widthStep}%` : preLeft.toString().indexOf("%") > -1 ? `${preLeft} + ${widthStep}%` : `${preLeft}px + ${widthStep}%`;
|
|
|
+ measure.stepList[j] = left;
|
|
|
+ }
|
|
|
+ metroMeasure[i].push({
|
|
|
+ index: j,
|
|
|
+ time,
|
|
|
+ // left: (measure.stepList[j] ? measure.stepList[j] + 'px' : (j + 1) * widthStep + '%'),
|
|
|
+ left: left?.indexOf("%") > -1 ? `calc(${left})` : left,
|
|
|
+ measureNumberXML: measure.measureNumberXML,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.log(error);
|
|
|
+ }
|
|
|
+ // console.log(metroList, metroMeasure);
|
|
|
+ // 5.得到所有的节拍时间
|
|
|
+ metronomeData.metroList = metroList;
|
|
|
+ metronomeData.metroMeasure = metroMeasure;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 计算拍子的时值
|
|
|
+function calculateMetroStep(arr: any[], m: any): number[] {
|
|
|
+ const measureLength = arr.reduce((total: number, item: any) => {
|
|
|
+ total += item._noteLength;
|
|
|
+ return total;
|
|
|
+ }, 0);
|
|
|
+ const clap = measureLength / m.numerator;
|
|
|
+ if (arr.length === 1) {
|
|
|
+ const wholeNote = arr[0].svgElelent
|
|
|
+ if (wholeNote && !wholeNote.isRest()) {
|
|
|
+ const measure_bbox = wholeNote?.attrs?.el?.parentElement?.parentElement?.getBoundingClientRect?.() || { x: 0, right: 0 };
|
|
|
+ let bbox = wholeNote?.attrs?.el?.getBoundingClientRect?.() || { x: 0 };
|
|
|
+ let stepWidth = Math.abs(measure_bbox.right - bbox.x) / m.numerator
|
|
|
+ let stepList: number[] = [];
|
|
|
+ for(let i = 0; i < m.numerator; i++){
|
|
|
+ // 是第一个小节,并且不是全音符,是弱起
|
|
|
+ if (m.measureNumberXML === 1 && wholeNote.duration !== "w") {
|
|
|
+ stepList.push(i === 0 ? bbox.x - measure_bbox.x : -1)
|
|
|
+ } else {
|
|
|
+ stepList.push(bbox.x - measure_bbox.x + i * stepWidth)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // console.log("🚀 ~ stepList:", stepList, m.measureNumberXML)
|
|
|
+ return stepList;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ // 开头是休止符
|
|
|
+ if (m.measureNumberXML === 1 && wholeNote && wholeNote.isRest()) {
|
|
|
+ const measure_bbox = wholeNote?.attrs?.el?.parentElement?.parentElement?.getBoundingClientRect?.() || { x: 0, right: 0 };
|
|
|
+ let bbox = wholeNote?.attrs?.el?.getBoundingClientRect?.() || { x: 0 };
|
|
|
+ let stepWidth = Math.abs(measure_bbox.right - bbox.x) / m.numerator
|
|
|
+ let stepList: number[] = [];
|
|
|
+ for(let i = -1; i < m.numerator - 1; i++){
|
|
|
+ stepList.push(bbox.x - measure_bbox.x + i * stepWidth)
|
|
|
+ }
|
|
|
+ // console.log(wholeNote?.attrs?.el, m.measureNumberXML)
|
|
|
+ // console.log("🚀 ~ stepList:", stepList, m.measureNumberXML)
|
|
|
+ return stepList;
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.log("🚀 ~ error:", error)
|
|
|
+ }
|
|
|
+
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+ // console.log("🚀 ~ arr", [...arr],`小节总时值: ${measureLength}`, clap, m.measureNumberXML);
|
|
|
+ let totalLength = 0;
|
|
|
+ let notes: any[] = [];
|
|
|
+ let stepList: number[] = [];
|
|
|
+ for (let i = 0; i < arr.length; i++) {
|
|
|
+ const item = arr[i];
|
|
|
+ item.index = i;
|
|
|
+ const noteLength = item._noteLength;
|
|
|
+ totalLength += noteLength;
|
|
|
+ // 大于一拍
|
|
|
+ const exceedStep = totalLength / clap;
|
|
|
+ // console.log(`note`, item?.svgElelent?.attrs?.el,notes.length,{noteLength, exceedStep,clap}, m.measureNumberXML)
|
|
|
+ if (exceedStep >= 1) {
|
|
|
+ totalLength -= clap;
|
|
|
+ // 一拍
|
|
|
+ const measure_bbox = item?.svgElelent?.attrs?.el?.parentElement?.parentElement?.getBoundingClientRect?.() || { x: 0 };
|
|
|
+ if (notes.length > 0) {
|
|
|
+ let bbox = notes[0]?.svgElelent?.attrs?.el?.querySelector('.vf-note')?.getBoundingClientRect?.() || { x: 0 };
|
|
|
+ let x: any = bbox.x - measure_bbox.x;
|
|
|
+ if ((notes[0]._noteLength / clap) >= 1) {
|
|
|
+ const nextNote = arr[notes[0].index + 1]?.svgElelent?.attrs?.el?.getBoundingClientRect?.() || { x: measure_bbox.right } || { x: 0 };
|
|
|
+ const stepWidth = Math.abs(bbox.x - nextNote.x) / 2;
|
|
|
+ x = bbox.x - measure_bbox.x + stepWidth;
|
|
|
+ // console.log(`音符超一拍`, notes[0]?.svgElelent?.attrs?.el, arr[notes[0].index + 1]?.svgElelent?.attrs?.el, bbox.x - nextNote.x, stepWidth, m.measureNumberXML);
|
|
|
+ }
|
|
|
+ // console.log(`一拍`, notes[0]?.svgElelent?.attrs?.el, m.measureNumberXML, notes[0]._noteLength , clap, 'aa')
|
|
|
+ stepList.push(x);
|
|
|
+ } else {
|
|
|
+ let bbox = item?.svgElelent?.attrs?.el?.querySelector('.vf-note')?.getBoundingClientRect?.() || { x: 0 };
|
|
|
+ let x: any = bbox.x - measure_bbox.x
|
|
|
+ // console.log(`一拍`, item?.svgElelent?.attrs?.el, m.measureNumberXML)
|
|
|
+ stepList.push(x);
|
|
|
+ }
|
|
|
+ notes = [];
|
|
|
+ let bbox = item?.svgElelent?.attrs?.el?.querySelector('.vf-note')?.getBoundingClientRect?.() || { x: 0 };
|
|
|
+ let x: any = bbox.x - measure_bbox.x;
|
|
|
+ let stepWidth = 0;
|
|
|
+ if (exceedStep > 1) {
|
|
|
+ // 二拍以上
|
|
|
+ const nextNote = arr[i + 1]?.svgElelent?.attrs?.el?.querySelector('.vf-note')?.getBoundingClientRect?.() || { x: measure_bbox.right } || { x: 0 };
|
|
|
+ stepWidth = Math.abs(bbox.x - nextNote.x) / Math.ceil(exceedStep);
|
|
|
+ // console.log("二拍以上 ~ nextNote:",bbox.x , nextNote.x,stepWidth, item?.svgElelent?.attrs?.el,arr[i + 1]?.svgElelent?.attrs?.el, exceedStep);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let j = 1; j < exceedStep; j++) {
|
|
|
+ totalLength -= clap;
|
|
|
+ // console.log(`超一拍`,item?.svgElelent?.attrs?.el, m.measureNumberXML)
|
|
|
+ stepList.push(x + stepWidth * j);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //有时值就将音符加入
|
|
|
+ if (totalLength > Number.EPSILON && totalLength > 0) {
|
|
|
+ notes.push(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ stepList = stepList.reduce((list: any[], n: number) => {
|
|
|
+ if (list.includes(n)) {
|
|
|
+ list.push(undefined as any);
|
|
|
+ } else {
|
|
|
+ list.push(n);
|
|
|
+ }
|
|
|
+ return list;
|
|
|
+ }, []);
|
|
|
+ // console.log("stepList", [...stepList], m.measureNumberXML);
|
|
|
+ // for (let i in stepList) {
|
|
|
+ // stepList[i] = stepList[i] / state.musicZoom
|
|
|
+ // }
|
|
|
+ console.log('🚀 ~ stepList:',stepList)
|
|
|
+ return stepList;
|
|
|
+}
|
|
|
+// 计算单声部多声轨的拍子的时值
|
|
|
+function calculateMutilpleMetroStep(arr: any[], m: any): number[] {
|
|
|
+ // console.log("🚀 ~ m:", [...m.svgs])
|
|
|
+ const step = m.time / m.numerator;
|
|
|
+ const measure_bbox = arr[0]?.svgElelent?.attrs?.el?.parentElement?.parentElement?.getBoundingClientRect?.() || { x: 0 };
|
|
|
+ if (arr.length === 1) {
|
|
|
+ const staveNote = m.svgs[0];
|
|
|
+ // 大于一拍
|
|
|
+ let bbox = staveNote?.attrs?.el?.getBoundingClientRect?.() || { x: 0 };
|
|
|
+ if (staveNote && !staveNote.isRest()) {
|
|
|
+ return [bbox.x - measure_bbox.x];
|
|
|
+ }
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+ // console.log("🚀 ~ arr", arr, step, m.measureNumberXML);
|
|
|
+ let total = 0;
|
|
|
+ let notes: any[] = [];
|
|
|
+ let stepList: number[] = [];
|
|
|
+ for (let i = 0; i < arr.length; i++) {
|
|
|
+ const item = arr[i];
|
|
|
+ item._index = i;
|
|
|
+ const noteTime = item.endtime - item.time;
|
|
|
+ total += noteTime;
|
|
|
+ let svgEle = m.svgs[i]?.attrs?.el;
|
|
|
+ // 大于一拍
|
|
|
+ let bbox = svgEle?.getBoundingClientRect?.() || { x: 0 };
|
|
|
+ // console.log(m.measureNumberXML, svgEle, i)
|
|
|
+ if (noteTime > step) {
|
|
|
+ total -= step;
|
|
|
+ // console.log('超过一拍了', notes, m.measureNumberXML)
|
|
|
+ let x = bbox.x - measure_bbox.x;
|
|
|
+ if (notes.length > 0) {
|
|
|
+ svgEle = m.svgs[notes[0]._index]?.attrs?.el;
|
|
|
+ bbox = svgEle?.getBoundingClientRect?.() || { x: 0 };
|
|
|
+ x = bbox.x - measure_bbox.x;
|
|
|
+ }
|
|
|
+ stepList.push(x);
|
|
|
+ notes = [];
|
|
|
+ } else {
|
|
|
+ notes.push(item);
|
|
|
+ }
|
|
|
+ // console.log(notes)
|
|
|
+ if (Math.abs(total - step) < 0.001) {
|
|
|
+ let x = bbox.x - measure_bbox.x;
|
|
|
+ if (notes.length > 0) {
|
|
|
+ svgEle = m.svgs[notes[0]._index]?.attrs?.el;
|
|
|
+ bbox = svgEle?.getBoundingClientRect?.() || { x: 0 };
|
|
|
+ x = bbox.x - measure_bbox.x;
|
|
|
+ }
|
|
|
+ // console.log("一拍",svgEle,notes,m.svgs, m.measureNumberXML);
|
|
|
+ stepList.push(x);
|
|
|
+ total = 0;
|
|
|
+ notes = [];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ stepList = stepList.reduce((list: any[], n: number) => {
|
|
|
+ if (list.includes(n)) {
|
|
|
+ list.push(undefined as any);
|
|
|
+ } else {
|
|
|
+ list.push(n);
|
|
|
+ }
|
|
|
+ return list;
|
|
|
+ }, []); //Array.from(new Set(stepList))
|
|
|
+ // console.log('stepList', stepList, m.measureNumberXML)
|
|
|
+ // for (let i in stepList) {
|
|
|
+ // stepList[i] = stepList[i] / state.musicZoom
|
|
|
+ // }
|
|
|
+ console.log('🚀 ~ stepList:',stepList)
|
|
|
+ return stepList;
|
|
|
+}
|
|
|
+
|
|
|
+// 延迟兼容处理
|
|
|
+function setCurrentTime(time: number) {
|
|
|
+ if (browserInfo.huawei || browserInfo.xiaomi) {
|
|
|
+ time += 0.125;
|
|
|
+ } else if (browserInfo.android) {
|
|
|
+ time += 0.11;
|
|
|
+ } else if (browserInfo.ios) {
|
|
|
+ time += 0.01;
|
|
|
+ }
|
|
|
+ return time;
|
|
|
+}
|
|
|
+
|
|
|
+// 自动隐藏光标提示
|
|
|
+function hideCursorTip() {
|
|
|
+ if (!tipsTimer) {
|
|
|
+ tipsTimer = setTimeout(() => {
|
|
|
+ metronomeData.cursorTips = ''
|
|
|
+ clearTimeout(tipsTimer)
|
|
|
+ tipsTimer = null
|
|
|
+ }, 2000);
|
|
|
+ } else {
|
|
|
+ clearTimeout(tipsTimer)
|
|
|
+ tipsTimer = setTimeout(() => {
|
|
|
+ metronomeData.cursorTips = ''
|
|
|
+ clearTimeout(tipsTimer)
|
|
|
+ tipsTimer = null
|
|
|
+ }, 2000);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export default Metronome;
|