Forráskód Böngészése

Merge branch 'feature-patch' into ktyq-test-new

TIANYONG 2 hónapja
szülő
commit
b8b8c8e075

+ 106 - 52
src/helpers/metronome.ts

@@ -42,6 +42,7 @@ export const metronomeData = reactive({
 	cursorTips: '' as string, // 光标模式提示文字
 	followAudioIndex: 1, // 当前的拍数
 	totalNumerator: 2, // 总拍数
+	firstBeatTypeArr:[] as number[] // 第一小节的节拍
 });
 
 watch(
@@ -331,58 +332,27 @@ class Metronome {
 					const m = {
 						measureNumberXML: measureNumberXML,
 						measureNumberIndex: measureListIndex,
+						CompoundTempo: note?.noteElement?.sourceMeasure?.CompoundTempo || "",
 						numerator: note?.noteElement?.sourceMeasure?.ActiveTimeSignature?.numerator || 0,
+						denominator: note?.noteElement?.sourceMeasure?.ActiveTimeSignature?.denominator || 0,
 						start: startTime,
 						end: noteEndTime,
 						time: noteEndTime - startTime,
 						stave_x: note?.noteElement?.sourceMeasure?.verticalMeasureList?.[0]?.stave?.x || 0,
-						end_x: note?.stave?.end_x || 0 || 0,
+						end_x: note?.stave?.end_x || 0,
 						stepList: [] as number[],
 						svgs: [] as any[],
 						isRestFlag: note.isRestFlag,
 					};
-					// 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 {
-						/**
-						 * bug:#9877
-						 * 多分轨合并显示,不同分轨的音符数量可能不同
-						 */
-						let measureArr = note.measures;
-						if (state.isCombineRender) {
-							measureArr = measureArr.filter((item: any) => item.MeasureNumberXML === m.measureNumberXML)
-						}
-						m.stepList = calculateMetroStep(measureArr, m);
+					/**
+					 * bug:#9877
+					 * 多分轨合并显示,不同分轨的音符数量可能不同
+					 */
+					let measureArr = note.measures;
+					if (state.isCombineRender) {
+						measureArr = measureArr.filter((item: any) => item.MeasureNumberXML === m.measureNumberXML)
 					}
+					m.stepList = calculateMetroStep(measureArr, m);					
 					measures.push(m);
 					xmlNumber = measureNumberXML;
 				}
@@ -403,30 +373,57 @@ class Metronome {
 		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);
+				// 87拍和45拍要根据小节返回的CompoundTempo特殊处理
+				const beatTypeArr = getBeatTypeArr(measure.numerator, measure.denominator, measure.CompoundTempo)
+				const CompoundTempoArr = beatTypeArr.map((beatType:number) => {
+					return Math.abs(beatType*measure.numerator)
+				})
+				if(i===0){
+					metronomeData.firstBeatTypeArr = beatTypeArr
+				}
 				metroMeasure[i] = [] as number[];
+				// 根据有几个拍子,划分成几份
+				const widthStep = 100 / (beatTypeArr.length+1);
+				// 当前拍子的组合数(2+3+2,3+2)中的数字
+				let beatNum = 0;
 				// console.log('stepList', [...measure.stepList], measure.measureNumberXML)
-				for (let j = 0; j < measure.numerator; j++) {
-					const time = noteStep * j + measure.start;
+				for (let j = 0; j < beatTypeArr.length; j++) {
+					// 累加
+					const beatMuit = Array(j).fill("").reduce((num:number,v:any,i:number) => {
+						return num += Math.abs(beatTypeArr[i])
+					}, 0) || 0
+					const time = measure.time * beatMuit + measure.start;
 					metroList.push(time);
 					let left = "";
-					if (measure.stepList[j]) {
-						left = measure.stepList[j] + "px";
+					// 当前拍子数对应的节拍位置索引
+					let currentIdx = 0;
+					if (j == 0) {
+						currentIdx = 0
+					} else {
+						beatNum += CompoundTempoArr[j-1];
+						currentIdx = beatNum
+					}
+					// 如果是87拍,并且是3+2+2的组合,第二拍的节拍指针需要定位到第四个音符的位置
+					// if (measure.numerator === 7 && measure.denominator === 8 && measure.CompoundTempo === '3+2+2' && j === 1) {
+					// 	currentIdx += 1;
+					// }					
+					if (measure.stepList[currentIdx]) {
+						left = measure.stepList[currentIdx] + "px";
 					} else {
 						const preLeft = measure.stepList[j - 1];
-						left = !preLeft ? `${widthStep}%` : preLeft.toString().indexOf("%") > -1 ? `${preLeft} + ${widthStep}%` : `${preLeft}px + ${widthStep}%`;
+						left = !preLeft || preLeft.toString().indexOf("%") > -1 ? `${widthStep*(j+1)}%` : `${preLeft}px + ${widthStep}%`;
 						measure.stepList[j] = left;
-					}
+					}	
 					metroMeasure[i].push({
+						isTick: beatTypeArr[j] < 0,
 						index: j,
 						time,
 						// left: (measure.stepList[j] ? measure.stepList[j] + 'px' : (j + 1) * widthStep + '%'),
 						left: left?.indexOf("%") > -1 ? `calc(${left})` : left,
 						measureNumberXML: measure.measureNumberXML,
 						isRestFlag: measure.isRestFlag,
+						stepList: measure.stepList,
+						isPercent: left?.indexOf("%") > -1, // 是否是根据小节宽度等分
 					});
 				}
 			}
@@ -442,6 +439,63 @@ class Metronome {
 	}
 }
 
+/** 获取节拍类型数组 */
+export function getBeatTypeArr(numerator?:number, denominator?:number, CompoundTempo?:string){
+	const speedBeatUnit = state.speedBeatUnit
+	const Numerator = numerator || state.osmd?.Sheet?.SheetPlaybackSetting?.Rhythm?.Numerator || 4
+	const Denominator = denominator || state.osmd?.Sheet?.SheetPlaybackSetting?.Rhythm?.Denominator || 4
+	let loopArr = []
+	// 规则 负数代表重声,正数代表轻声
+	switch (`${Numerator}/${Denominator}`) {
+		case "2/2":
+			loopArr = [-1/2, 1/2]
+			break;		
+		case "3/2":
+			loopArr = [-1/3, 1/3, 1/3]
+			break;
+		case "5/4":
+			//5/4拍根据谱面的CompoundTempo来特殊处理
+			if(CompoundTempo==="2+3"){
+				loopArr = [-1/5, 1/5, -1/5, 1/5, 1/5]
+			}else{
+				loopArr = [-1/5, 1/5, 1/5, -1/5, 1/5]
+			}
+			break;
+		case "3/8":
+			// 3/8拍 速度为浮点4分音符时候特殊处理
+			if(speedBeatUnit==="1/4."){
+				loopArr = [-1/1]
+			}else{
+				loopArr = [-1/3, 1/3, 1/3]
+			}
+			break;
+		case "6/8":
+			loopArr = [-1/2, 1/2]
+			break;	
+		case "7/8":
+			//7/8拍根据谱面的CompoundTempo来特殊处理
+			if(CompoundTempo==="2+2+3"){
+				loopArr = [-2/7, 2/7, 3/7]
+			}else if(CompoundTempo==="2+3+2"){
+				loopArr = [-2/7, 3/7, 2/7]
+			}else{
+				loopArr = [-3/7, 2/7, 2/7]
+			}
+			break;	
+		case "9/8":
+			loopArr = [-3/9, 3/9, 3/9]
+				break;				
+		default:
+			loopArr.push(-1/Numerator);
+			for (let i = 1; i < Numerator; i++) {
+				loopArr.push(1/Numerator);
+			}
+			break;
+	}
+	// console.log(loopArr, "loopArr")
+	return loopArr
+}
+
 // 计算拍子的时值
 function calculateMetroStep(arr: any[], m: any): number[] {
 	const measureLength = arr.reduce((total: number, item: any) => {

+ 29 - 0
src/helpers/svgToPng.ts

@@ -0,0 +1,29 @@
+// 将svg转成png
+export const getSvgPngToSize = (osmd: any) => {
+    if (osmd) {
+      if (osmd.Drawer.Backends.length > 0) {
+        var imgList = []
+        
+        for (var idx = 0, len = osmd.Drawer.Backends.length; idx < len; idx++) {
+          var backend = osmd.Drawer.Backends[idx]
+          var state = backend.ctx.state;
+          var width = backend.ctx.width / state.scale.x;
+          var height = backend.ctx.height / state.scale.y;
+          const textX = width - 90,textY = height - 90;
+          const textDom = `<g><text x="${textX}" y="${textY}" stroke-width="3" fill="#000000" stroke="none" stroke-dasharray="none" font-family="Times New Roman" font-size="36px" font-weight="bold" font-style="none">${idx+1}/${len}</text></g>`
+          backend.ctx.svg.innerHTML = backend.ctx.svg.innerHTML + textDom;
+          var cont = new XMLSerializer().serializeToString(
+            backend.ctx.svg
+          )
+          imgList.push({
+            img: cont,
+            width: width,
+            height: height,
+          })
+        }
+        return imgList
+      }
+    } else {
+      console.log('没有OSMD')
+    }
+}

+ 1 - 1
src/page-instrument/evaluat-model/evaluat-result/index.tsx

@@ -165,7 +165,7 @@ export default defineComponent({
                   </div>
                 </div>
               )}
-              <div class={styles.tips}>{evaluatingData.resultData.clxtip}</div>
+              <div class={styles.tips}>{state.isPercussion ? evaluatingData.resultData.djytip : evaluatingData.resultData.clxtip}</div>
               <div class={styles.ctrls}>
                 <img src={zlycImg} class={[styles.ctrlsBtn, "evaluting-result-2"]} onClick={() => emit("close", "tryagain")} />
                 {

+ 17 - 1
src/page-instrument/header-top/settting/index.tsx

@@ -87,7 +87,23 @@ export default defineComponent({
                                     <div class={styles.tit}>循环播放</div>
                                     <Switch v-model={state.setting.repeatAutoPlay}></Switch>
                                 </div>
-                        }                        
+                        }  
+                        {   !state.isCombineRender &&                    
+                            <div class={styles.cellBox}>
+                                <div class={styles.tit}>合并休止小节</div>
+                                <Switch 
+                                    v-model={state.setting.combineMultipleRest}
+                                    onChange={ async (value) => {
+                                        await checkMoveNoSave();
+                                        headTopData.settingMode = false
+                                        const _time = setTimeout(() => {
+                                            clearTimeout(_time)
+                                            refreshMusicSvg();
+                                        }, 100);
+                                    }}
+                                ></Switch>
+                            </div>
+                        }                                              
                         {
                             state.isSingleLine && state.modeType === "practise" && !state.isCombineRender && !state.isPercussion && 
                                 <div class={styles.cellBox}>

+ 34 - 2
src/page-instrument/view-detail/index.tsx

@@ -40,6 +40,7 @@ import EmptyMusic, { isEmptyMusicShow } from "./emptyMusic";
 import { position } from "html2canvas/dist/types/css/property-descriptors/position";
 import Loading from "./loading"
 import LoadingCss from "./loadingCss"
+import { getSvgPngToSize } from "/src/helpers/svgToPng"
 // import bgJson from "./images/index.json";
 
 // const DelayCheck = defineAsyncComponent(() =>
@@ -210,6 +211,22 @@ export default defineComponent({
       console.timeEnd("渲染加载耗时");
       detailData.skeletonLoading = false;
       state.osmd = osmd;
+      // 预览模式不需要往下执行
+      if (state.isPreView) {
+        // 管乐迷曲谱详情页,需要下载A4尺寸的图片
+        setTimeout(() => {
+          if (query.downPng === 'A4' && state.partIndex != 999) {
+            const imgList = getSvgPngToSize(state.osmd)
+            console.log('A4', imgList)
+            window.parent.postMessage({
+              api: 'musicStaffRender',
+              loading: false,
+              osmdImg: imgList
+            }, '*');
+          }
+        }, 100);
+        return;
+      }      
       // 没有设置速度使用读取的速度
       if (state.originSpeed === 0) {
         state.originSpeed = state.speed = (osmd as any).bpm || osmd.Sheet.userStartTempoInBPM || 100;
@@ -246,7 +263,9 @@ export default defineComponent({
       try {
         metronomeData.metro = new Metronome();
         metronomeData.metro.init(state.times);
-      } catch (error) {}
+      } catch (error) {
+        console.log(error, "err")
+      }
 
       // 需要向外面(iframe)派发计时器数据的时候触发
       if (query.isbeatTimes) {
@@ -300,7 +319,7 @@ export default defineComponent({
        * 设置节拍器,跟练需要播放系统节拍器,所以不需要判断needTick状态
        */
       // if (state.needTick) {
-      handleInitTick(osmd?.Sheet?.SheetPlaybackSetting?.Rhythm?.Numerator || 4, osmd?.Sheet?.SheetPlaybackSetting?.Rhythm?.denominator);
+      handleInitTick();
       // }
       // api_cloudLoading();
       // state.playBtnDirection = query.imagePos === 'left' ? 'left' : 'right';
@@ -310,6 +329,18 @@ export default defineComponent({
       // } else {
       //   state.musicScoreBtnDirection = state.playBtnDirection;
       // }
+      // 曲谱详情页,需要下载A4尺寸的图片
+      setTimeout(() => {
+        if (query.downPng === 'A4' && state.partIndex != 999) {
+          const imgList = getSvgPngToSize(state.osmd)
+          console.log('A4', imgList)
+          window.parent.postMessage({
+            api: 'musicStaffRender',
+            loading: false,
+            osmdImg: imgList
+          }, '*');
+        }
+      }, 100);      
       state.musicScoreBtnDirection = state.playBtnDirection;
       state.musicRendered = true;
 
@@ -326,6 +357,7 @@ export default defineComponent({
       try{
         handleRendered(osmd)
       }catch(err:any){
+        console.log(err, "err")
         // 需要向外面(iframe)派发计时器数据的时候触发
         if(query.isbeatTimes){
           console.log("webApi_beatTimes",err)

+ 6 - 1
src/page-instrument/view-evaluat-report/index.tsx

@@ -83,6 +83,7 @@ export default defineComponent({
       headerHide: false,
       musicalNotesPlayStats: [] as any[],
       userMeasureScore: {} as any,
+      isSpecialReport: false, // 是否是特殊的评测记录(含有listent、play等曲子的评测记录,非选段状态下可能返回的评测数据不是从第1小节开始的,这种情况需要兼容处理)
     });
     const getAPPData = async () => {
       const screenData = await isSpecialShapedScreen();
@@ -269,7 +270,10 @@ export default defineComponent({
       // console.log(1111,notes)
       for (const note of notes) {
         const idx = note.musicalNotesIndex !== undefined ? note.musicalNotesIndex : note.index;
-        const active = allNote.value[idx];
+        let active = allNote.value[idx];
+        if (detailData.isSpecialReport) {
+          active = allNote.value.find((item: any) => item.i === idx)
+        }        
         setTimeout(() => {
           if (useedid.value.includes(active.id)) {
             return;
@@ -412,6 +416,7 @@ export default defineComponent({
       beams.forEach((item: any) => {
         item.classList.add(styles.beam);
       });
+      detailData.isSpecialReport = startMeasureNum > 0 && detailData.musicalNotesPlayStats?.[0]?.musicalNotesIndex != 0;
       //setPathColor();
       setViewColor();
       // setMearureColor();

+ 27 - 6
src/state.ts

@@ -216,6 +216,8 @@ const state = reactive({
     reactionTimeMs: 0,
     /** 节拍器音量 */
     beatVolume: 50,
+    /** 合并休止小节 */
+    combineMultipleRest: true,    
   },
   /** 后台设置的基准评测频率 */
   baseFrequency: 440,
@@ -373,6 +375,8 @@ const state = reactive({
   speedIcon: 'speed3', // 默认取1/4拍的图片
   isSingleMutliTrack: false, // 是否是单声轨多声部的声轨
   showWorkDonePop: false, // 显示需要提交练习作业弹窗
+  /** 顶部栏高度 */
+  headTopHeight: 0,
 });
 const browserInfo = browser();
 let offset_duration = 0;
@@ -1096,7 +1100,7 @@ export const hanldeDirectSelection = (list: any[]) => {
     console.log('选段小节', state.section)
   }, 0);
 };
-let offsetTop = 0;
+let offsetTop = 0, musicScrollTop = 0;
 /**
  * 窗口内滚动到音符的区域
  * @param isScroll 可选: 强制滚动到顶部, 默认: false
@@ -1112,19 +1116,33 @@ export const scrollViewNote = (resetTop?: boolean) => {
     offsetTop = 0;
   }
   const domId = "vf" + noteId;
-  const cursorElement: any = noteId ? document.querySelector(`[data-vf=${domId}]`)?.parentElement : document.getElementById('restDot')?.parentElement;
+  // 合并休止小节没有音符,取小节的位置,否则取音符指针位置
+  const cursorElement: any = !noteId && state.times[state.activeNoteIndex]?.totalMultipleRestMeasures ? document.querySelector(`.measureIndex_${state.activeMeasureIndex}`) : document.querySelector(`[data-vf=${domId}]`)?.parentElement;
   const musicAndSelection = document.getElementById(state.scrollContainer)!;
+  if (!state.headTopHeight) {
+    state.headTopHeight = document.querySelector('.headHeight')?.getBoundingClientRect()?.height || 100;
+  }
+  if (!cursorElement || !musicAndSelection) {
+    return
+  }
   // offsetTop = musicAndSelection.scrollTop || offsetTop;
   const noteCenterOffsetTop = cursorElement ? cursorElement?.offsetTop + (cursorElement?.offsetHeight/2) : 0;
   // console.log('滑动',offsetTop, noteCenterOffsetTop)
-  if (!cursorElement || !noteCenterOffsetTop || !musicAndSelection || offsetTop === noteCenterOffsetTop || Math.abs(offsetTop - noteCenterOffsetTop) < 30) return;
-  offsetTop = noteCenterOffsetTop;
-  if (offsetTop > 100) {
+  if (Math.abs(musicAndSelection?.scrollTop - musicScrollTop) > 30) {
+    // 手动滑动谱面,重新播放需要滚动到对应位置
+  } else {
+    if (offsetTop === cursorElement.offsetTop || Math.abs(offsetTop - cursorElement.offsetTop) < 30) return;
+  }
+  // offsetTop = noteCenterOffsetTop;
+  offsetTop = cursorElement.offsetTop;
+  if (offsetTop > (state.headTopHeight + 30)) {
+    musicScrollTop = (offsetTop - state.headTopHeight - 30) * state.musicZoom
     musicAndSelection.scrollTo({
-      top: (offsetTop - 100) * state.musicZoom,
+      top: (offsetTop - state.headTopHeight - 30) * state.musicZoom,
       behavior: "smooth",
     });
   } else {
+    musicScrollTop = 0
     musicAndSelection.scrollTo({
       top: 0,
       behavior: "smooth",
@@ -1265,6 +1283,9 @@ function xmlToTracks(xmlString: string) {
   const partNames = Array.from(xmlParse.getElementsByTagName('part-name'));
   return partNames.reduce((arr: string[], item) => {
     const textContent = item?.textContent?.trim()
+    if (textContent?.toLocaleLowerCase() === "common") {
+      (window as any).HasCommonTrack = true;
+    }
     if (textContent?.trim()?.toLocaleLowerCase() !== "common" && textContent) {
       arr.push(textContent)
     }

+ 1 - 1
src/view/audio-list/index.tsx

@@ -264,7 +264,7 @@ async function mergeBeatAudio(music?:string){
 		const beatsTime:number[] = []
 		metronomeData.metroMeasure.map(measures=>{
 			measures.map((item:any)=>{
-				beats.push(item.index===0?tickBuff!:tockBuff!)
+				beats.push(item.isTick?tickBuff!:tockBuff!)
 				beatsTime.push(item.time + silenceDuration) // xml 计算的时候 加上空白的时间
 			})
 		})

+ 5 - 0
src/view/evaluating/evaluatResult.ts

@@ -54,6 +54,7 @@ const icons = [
 		mome: "敢于尝试",
 		clxImg: clx1,
 		clxtip: "你的演奏不太好,音准和完整性还需加强,再练一练吧~",
+		djytip: "你的演奏不太好,节奏还需加强,再练一练吧~",
 		clxmome: "敢于尝试"
 	},
 	{
@@ -62,6 +63,7 @@ const icons = [
 		mome: "还要加油哦~",
 		clxImg: clx2,
 		clxtip: "你的演奏还不熟练,音准和完整性还需加强,加紧训练才能有好成绩哦~",
+		djytip: "你的演奏还不熟练,节奏把握不太理想,加紧训练才能有好成绩哦~",
 		clxmome: "还要加油哦~"
 	},
 	{
@@ -70,6 +72,7 @@ const icons = [
 		mome: "突破自我",
 		clxImg: clx3,
 		clxtip: "你的演奏还不流畅,音准和节奏还需加强,科学的练习才能更完美哦~",
+		djytip: "你的演奏还不流畅,部分节奏需要勤加练习,科学的练习才能更完美哦~",
 		clxmome: "突破自我"
 	},
 	{
@@ -78,6 +81,7 @@ const icons = [
 		mome: "崭露头角",
 		clxImg: clx4,
 		clxtip: "你的演奏还不错,继续加油吧,加强音准,离完美就差一步啦~",
+		djytip: "你的演奏还不错,节奏还有些小瑕疵,离完美就差一步啦~",
 		clxmome: "崭露头角"
 	},
 	{
@@ -86,6 +90,7 @@ const icons = [
 		mome: "你很棒",
 		clxImg: clx5,
 		clxtip: "你的演奏非常不错,音准的把握和节奏稍有瑕疵,完整性把握的很好~",
+		djytip: "你的演奏非常不错,距离完成仅有一步之遥~",
 		clxmome: "你很棒"
 	},
 ];

+ 8 - 2
src/view/music-score/index.tsx

@@ -109,7 +109,7 @@ export default defineComponent({
 				// pageBackgroundColor: '#609FCF',
 				renderSingleHorizontalStaffline: state.isSingleLine ? true : false,
 				// autoGenerateMultipleRestMeasuresFromRestMeasures: state.isSingleLine ? false : true, // 连续休止小节是否合并显示
-				autoGenerateMultipleRestMeasuresFromRestMeasures: true,
+				autoGenerateMultipleRestMeasuresFromRestMeasures: state.setting.combineMultipleRest, // 是否自动合并休止小节
 				drawLyrics: ((state.playType === 'sing' || !state.isEvxml) && !state.isSimplePage) ? true : false, // 演唱模式才渲染歌词,simple页面不显示歌词
 				// darkMode: true, // 暗黑模式
 				// pageFormat: 'A4_P',
@@ -180,7 +180,13 @@ export default defineComponent({
 			// if (props.isDownXml) {
 			// 	osmd.setPageFormat('794x1065')
 			// }
-			osmd.zoom = state.zoom;
+			if (query.downPng === 'A4') {
+				osmd.EngravingRules.PageTopMargin = 5
+				osmd.setPageFormat('794x1100')
+				osmd.zoom = query.zoom || 0.3;
+			} else {
+				osmd.zoom = state.zoom;
+			}
 			osmd.render();
 			console.log("🚀 ~ osmd:", osmd)
 			emit("rendered", osmd);

+ 1 - 1
src/view/plugins/toggleMusicSheet/choosePartName/index.tsx

@@ -76,7 +76,7 @@ export default defineComponent({
           <div class={styles.pickerContent}>
             <div class={styles.pickerBox}>
               {
-                state.isScoreRender &&
+                state.isScoreRender && state.modeType === "practise" && 
                   <>
                     {/* <div class={styles.titCon}>
                       <div class={styles.tit}>选择总谱</div>

+ 9 - 1
src/view/selection/index.tsx

@@ -307,9 +307,17 @@ export default defineComponent({
 											styles.position,
 											// scoreItem ? `scoreItemLeve${scoreItem.leve}` : "", // 去掉评测小节得分的背景色
 											(state.platform === IPlatform.PC && state.zoom > 0.8) ? styles.linePC : '',
+											`measureIndex_${item.MeasureNumberXML}`
 										]}
 										style={item.staveBox}
-										onClick={() => handleSelection(item)}
+										onClick={() => {
+											// 当为连续休止小节的结束选段的时候 应该传休止小节 结束的位置
+											let staveItem = item
+											if(state.section.length === 1 && item.totalMultipleRestMeasures > 0){
+												staveItem = selectData.staves[index + item.totalMultipleRestMeasures - 1]
+											}
+											handleSelection(staveItem)	
+										}}
 									>
 										{/* {lineShow && (
 											<div style={{height: selectData.measureHeight + 'px', position: 'relative'}}>

+ 18 - 15
src/view/tick/index.tsx

@@ -7,6 +7,7 @@ import state from "/src/state";
 import { browser } from "/src/utils/index";
 import tickWav from "/src/assets/tick.mp3";
 import tockWav from "/src/assets/tock.mp3";
+import { metronomeData } from "/src/helpers/metronome"
 
 const tickData = reactive({
 	len: 0,
@@ -14,7 +15,7 @@ const tickData = reactive({
 	reduceLen: 0,
 	tickEnd: false,
 	/** 节拍器时间 */
-	beatLengthInMilliseconds: 0,
+	beatLengthInMilliseconds: [] as number[],
 	index: 0,
 	show: false
 });
@@ -70,7 +71,7 @@ const handlePlay = (i: number, source: any | null) => {
 		} else {
 			_time=setTimeout(() => {
 				tickPlayCb(i, resolve, source);
-			}, tickData.beatLengthInMilliseconds);
+			}, Math.abs(tickData.beatLengthInMilliseconds[i-1])*1000/state.basePlayRate/state.originAudioPlayRate);
 		}
 	});
 };
@@ -94,18 +95,21 @@ const createAudio = (src: string): Promise<HTMLAudioElement | null> => {
 	});
 };
 
-/** 设置节拍器
- * @param beat 节拍数
- * @param denominator 节拍器分母
+/*
+ * 设置节拍器
  */
-export const handleInitTick = (beat: number, denominator?: number) => {
-	tickData.len = beat;
-	tickData.denominator = denominator;
-	// 节拍器的个数除以2 直到小于等于4为止 
-	while (beat > 4 && beat % 2 === 0) {
-        beat = beat / 2;
-    }
-	tickData.reduceLen = beat
+export const handleInitTick = () => {
+	const beatLen = metronomeData.firstBeatTypeArr.length * (state.repeatedBeats ? 2 : 1)
+	const beatLengthInMilliseconds = metronomeData.firstBeatTypeArr.map(item=>{
+		return item*state.times[0].measureLength
+	})
+	tickData.beatLengthInMilliseconds = [...beatLengthInMilliseconds,...(state.repeatedBeats ? beatLengthInMilliseconds : [])]
+	tickData.len = beatLen;
+	// // 节拍器的个数除以2 直到小于等于4为止 
+	// while (beat > 4 && beat % 2 === 0) {
+    //     beat = beat / 2;
+    // }
+	tickData.reduceLen = beatLen
 };
 
 /** 开始节拍器 */
@@ -114,12 +118,11 @@ export const handleStartTick = async () => {
 	tickData.show = true;
 	tickData.tickEnd = false;
 	tickData.index = 0;
-	tickData.beatLengthInMilliseconds = tickData.denominator ? 4 / tickData.denominator * (60 / state.speed) * 1000 : (60 / state.speed) * 1000;
 	for(let i = 0; i <= useLen.value; i++){
 		// 提前结束, 直接放回false
 		if (tickData.tickEnd) return false;
 		// Audio 标签播放音频
-		const source = i === 0 ? audioData.tick : i === useLen.value ? null : audioData.tock;
+		const source = tickData.beatLengthInMilliseconds[i] < 0 ? audioData.tick : i === useLen.value ? null : audioData.tock;
 		await handlePlay(i, source)
 	}
 	tickData.show = false;