浏览代码

Merge branch 'feature-tianyong-newVersion' into ktyq-online-new

TIANYONG 10 月之前
父节点
当前提交
710990febb

+ 204 - 1
src/helpers/customMusicScore.ts

@@ -1093,4 +1093,207 @@ export const setCustomNoteRealValue = () => {
     if (["12667", "12673"].includes(detailId)){
         customData.customNoteCurrentTime = true
     }
-};
+};
+
+/** 转换简谱的全休止符和二分休止符 */
+export const transferJianNote = (measure: any, divisions: number, preBeats: number, preBeatType: number) => {
+	const multipleXs = preBeatType / 4;
+	const notes = measure.getElementsByTagName("note")
+	for (const note of notes) {
+		// 是否需要考虑带上附点
+		let needAddDot = true;
+		const noteType = note.getElementsByTagName("type")?.[0]?.textContent || '';
+		if ((noteType === 'whole' || noteType === 'half') && note.getElementsByTagName("rest").length) {
+			// 4/4拍,3/4拍
+			if (preBeatType === 4) {
+				let maxNumber = noteType === 'half' ? 2 : preBeats / multipleXs;
+				if (noteType === 'whole') {
+					// 有可能是全休止符,但是该小节又不是整小节都休止,此时这个全休止符不能按照整小节休止来计算
+					const noteDivisions = parseInt(note.getElementsByTagName("duration")[0]?.textContent);
+					if (noteDivisions/divisions !== preBeats) {
+						maxNumber = 4;
+					} else {
+						// 满足了时值,则不需要考虑加上附点
+						needAddDot = false;
+					}
+				}
+				// 如果音符带附点,需要判断处理下
+				if (note.getElementsByTagName("dot").length && needAddDot) {
+					maxNumber = noteType === 'whole' ? maxNumber + 2 : maxNumber + 1;
+				}
+				if (!Number.isInteger(maxNumber)) {
+					return;
+				}
+				// console.log('几个1/4音符',maxNumber)
+				let quarterNoteNumber = 1;
+				while (quarterNoteNumber <= maxNumber) {
+					const newnote = document.createElement('note');
+					newnote.innerHTML = `
+					<rest></rest>
+					<duration>${divisions}</duration>
+					<voice>1</voice>
+					<type>quarter</type>`
+					measure.insertBefore(newnote, note);
+					quarterNoteNumber += 1;
+				};
+				measure.removeChild(note);
+			} else if (preBeats === 3 && preBeatType === 8) {
+				const maxNumber = noteType === 'half' ? 2 : 3;
+				let quarterNoteNumber = 1;
+				while (quarterNoteNumber <= maxNumber) {
+					const newnote = document.createElement('note');
+					newnote.innerHTML = `
+					<rest></rest>
+					<duration>${divisions/2}</duration>
+					<voice>1</voice>
+					<type>eighth</type>`
+					measure.insertBefore(newnote, note);
+					quarterNoteNumber += 1;
+				};
+				measure.removeChild(note);
+			} else if (preBeats === 5 && preBeatType === 8) {
+				if (noteType === 'whole') {
+					const newnote = document.createElement('note');
+					newnote.innerHTML = `
+					<rest></rest>
+					<duration>${divisions+divisions/2}</duration>
+					<voice>1</voice>
+					<type>quarter</type>
+					<dot></dot>`
+					measure.insertBefore(newnote, note);
+					const newnote2 = document.createElement('note');
+					newnote2.innerHTML = `
+					<rest></rest>
+					<duration>${divisions}</duration>
+					<voice>1</voice>
+					<type>quarter</type>`
+					measure.insertBefore(newnote2, note);
+					measure.removeChild(note);
+				} else if (noteType === 'half') {
+					dealDotHalfNote(measure, divisions, note)
+				}
+			} else if (preBeats === 6 && preBeatType === 8) {
+				if (noteType === 'whole') {
+					const maxNumber = 2;
+					let quarterNoteNumber = 1;
+					while (quarterNoteNumber <= maxNumber) {
+						const newnote = document.createElement('note');
+						newnote.innerHTML = `
+						<rest></rest>
+						<duration>${divisions+divisions/2}</duration>
+						<voice>1</voice>
+						<type>quarter</type>
+						<dot></dot>`
+						measure.insertBefore(newnote, note);
+						quarterNoteNumber += 1;
+					};
+					measure.removeChild(note);
+				} else if (noteType === 'half') {
+					dealDotHalfNote(measure, divisions, note)		
+				}
+			} else if (preBeats === 7 && preBeatType === 8) {
+				if (noteType === 'whole') {
+					const newnote2 = document.createElement('note');
+					newnote2.innerHTML = `
+					<rest></rest>
+					<duration>${divisions+divisions/2}</duration>
+					<voice>1</voice>
+					<type>quarter</type>
+					<dot></dot>`
+					measure.insertBefore(newnote2, note);         					
+					const maxNumber = 2;
+					let quarterNoteNumber = 1;
+					while (quarterNoteNumber <= maxNumber) {
+						const newnote = document.createElement('note');
+						newnote.innerHTML = `
+						<rest></rest>
+						<duration>${divisions}</duration>
+						<voice>1</voice>
+						<type>quarter</type>`
+						measure.insertBefore(newnote, note);
+						quarterNoteNumber += 1;
+					};
+					measure.removeChild(note);
+				} else if (noteType === 'half') {
+					dealDotHalfNote(measure, divisions, note)
+				}
+			} else if (preBeats === 9 && preBeatType === 8) {
+				if (noteType === 'whole') {
+					const maxNumber = 3;
+					let quarterNoteNumber = 1;
+					while (quarterNoteNumber <= maxNumber) {
+						const newnote = document.createElement('note');
+						newnote.innerHTML = `
+						<rest></rest>
+						<duration>${divisions+divisions/2}</duration>
+						<voice>1</voice>
+						<type>quarter</type>
+						<dot></dot>`
+						measure.insertBefore(newnote, note);
+						quarterNoteNumber += 1;
+					};
+					measure.removeChild(note);
+				} else if (noteType === 'half') {
+					dealDotHalfNote(measure, divisions, note)
+				}
+			} else if (preBeats === 12 && preBeatType === 8) {
+				if (noteType === 'whole') {
+					const maxNumber = 4;
+					let quarterNoteNumber = 1;
+					while (quarterNoteNumber <= maxNumber) {
+						const newnote = document.createElement('note');
+						newnote.innerHTML = `
+						<rest></rest>
+						<duration>${divisions+divisions/2}</duration>
+						<voice>1</voice>
+						<type>quarter</type>
+						<dot></dot>`
+						measure.insertBefore(newnote, note);
+						quarterNoteNumber += 1;
+					};
+					measure.removeChild(note);
+				} else if (noteType === 'half') {
+					dealDotHalfNote(measure, divisions, note)
+				}
+			}
+		} 
+	}
+}
+
+/** 八几排的小节,二分休止符带附点 */
+const dealDotHalfNote = (measure: any, divisions: number, note: any) => {
+	// 如果音符带附点,需要判断处理下
+	if (note.getElementsByTagName("dot").length) {
+		const maxNumber = 2;
+		let quarterNoteNumber = 1;
+		while (quarterNoteNumber <= maxNumber) {
+			const newnote = document.createElement('note');
+			newnote.innerHTML = `
+			<rest></rest>
+			<duration>${divisions+divisions/2}</duration>
+			<voice>1</voice>
+			<type>quarter</type>
+			<dot></dot>`
+			measure.insertBefore(newnote, note);
+			quarterNoteNumber += 1;
+		};
+		measure.removeChild(note);
+	} else {
+		const newnote = document.createElement('note');
+		newnote.innerHTML = `
+		<rest></rest>
+		<duration>${divisions+divisions/2}</duration>
+		<voice>1</voice>
+		<type>quarter</type>
+		<dot></dot>`
+		measure.insertBefore(newnote, note);
+		const newnote2 = document.createElement('note');
+		newnote2.innerHTML = `
+		<rest></rest>
+		<duration>${divisions/2}</duration>
+		<voice>1</voice>
+		<type>eighth</type>`
+		measure.insertBefore(newnote2, note);
+		measure.removeChild(note);
+	}
+}

+ 10 - 52
src/helpers/formateMusic.ts

@@ -2,6 +2,7 @@ import dayjs from "dayjs";
 import duration from "dayjs/plugin/duration";
 import state, { customData } from "/src/state";
 import { browser } from "../utils/index";
+import { transferJianNote } from "/src/helpers/customMusicScore"
 import {
 	isSpecialMark,
 	isSpeedKeyword,
@@ -779,60 +780,13 @@ export const formatXML = (xml: string, xmlUrl?: string): string => {
           <voice>1</voice>
           <type>whole</type>
         </note>`;
+		} else if (state.musicRenderType !== 'staff') {
+			transferJianNote(measure, divisions, preBeats, preBeatType)
 		}
-		// if (state.musicRenderType !== 'staff') {
-		// 	transferJianNote(measure, divisions, preBeats, preBeatType)
-		// }
 	}
 	return new XMLSerializer().serializeToString(xmlParse);
 };
 
-/** 转换简谱的全休止符和二分休止符 */
-export const transferJianNote = (measure: any, divisions: number, preBeats: number, preBeatType: number) => {
-	const multipleXs = preBeatType / 4;
-	const notes = measure.getElementsByTagName("note")
-	for (const note of notes) {
-		const noteType = note.getElementsByTagName("type")?.[0]?.textContent || '';
-		if (noteType === 'whole' || noteType === 'half') {
-			// 4/4拍
-			if (preBeats === 4 && preBeatType === 4) {
-
-			} else if (preBeats === 3 && preBeatType === 4) {
-
-			} else if (preBeats === 3 && preBeatType === 8) {
-
-			} else if (preBeats === 3 && preBeatType === 8) {
-
-			} else if (preBeats === 5 && preBeatType === 8) {
-
-			} else if (preBeats === 6 && preBeatType === 8) {
-
-			} else if (preBeats === 9 && preBeatType === 8) {
-
-			} else if (preBeats === 12 && preBeatType === 8) {
-
-			}
-
-			const maxNumber = noteType === 'half' ? 2 : preBeats / multipleXs;
-			if (!Number.isInteger(maxNumber)) {
-				return;
-			}
-			// console.log('几个1/4音符',maxNumber)
-			let quarterNoteNumber = 1;
-			while (quarterNoteNumber <= maxNumber) {
-				const newnote = document.createElement('note');
-				newnote.innerHTML = `
-				<rest></rest>
-				<duration>${divisions}</duration>
-				<voice>1</voice>
-				<type>quarter</type>`
-				measure.insertBefore(newnote, note);
-				quarterNoteNumber += 1;
-			};
-			measure.removeChild(note);
-		} 
-	}
-}
 
 /** 获取所有音符的时值,以及格式化音符 */
 export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
@@ -1440,6 +1394,7 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 				notBeatTime: state.isEvxml && evNoteStartTime ? retain(evNoteStartTime) : retain(relativeTime + (state.isOpenMetronome ? fixtime - xmlMp3BeatFixTime : fixtime)), // 不含节拍器的 音符开始时间
 				notBeatEndTime: state.isEvxml && evNoteEndTime ? retain(evNoteEndTime) : retain(relaEndtime + (state.isOpenMetronome ? fixtime - xmlMp3BeatFixTime : fixtime)) // 不含节拍器的 音符结束时间
 			};
+			// console.log('当前的小节',nodeDetail.MeasureNumberXML,totalMultipleRestMeasures,multipleRestMeasures)
 			// 如果是妙极客的曲子,并且第二遍循环播放需要等待时间,并且是第二遍循环的第一个小节的第一个音符
 			// if (state.isEvxml && state.secondEvXmlBeginTime && nodeDetail.i > 0 && nodeDetail.MeasureNumberXML === 1 && nodeDetail.noteId === 0) {
 			// 	nodeDetail.time = nodeDetail.time + state.secondEvXmlBeginTime;
@@ -1614,16 +1569,19 @@ const customizationXml = (xmlParse: any) => {
 	const measures: any[] = Array.from(xmlParse.getElementsByTagName("measure"));
 	const notes: any[] = Array.from(xmlParse.getElementsByTagName("note"));
 
-	// 获取音符最多的歌词数,用于自定义循环播放次数
-	let maxLyricNum = 0;
+	// 获取音符最多的歌词数,time最多的次数,取两者的最大值,用于自定义循环播放次数
+	let maxLyricNum = 0, maxTimeNum = 0;
 	if (notes && notes.length) {
 		for (const note of notes) {
 			if (maxLyricNum < note.getElementsByTagName("lyric").length) {
 				maxLyricNum = note.getElementsByTagName("lyric").length
 			}
+			if (maxTimeNum < note.getElementsByTagName("time").length) {
+				maxTimeNum = note.getElementsByTagName("time").length
+			}
 		}
 	}
-	state.maxLyricNum = maxLyricNum;
+	state.maxLyricNum = Math.max(maxLyricNum, maxTimeNum);
 	// state.osmd.EngravingRules.DYCustomRepeatCount = maxLyricNum;
 	;(window as any).DYCustomRepeatCount = state.maxLyricNum;
 	console.log('歌词次数',maxLyricNum)

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

@@ -49,7 +49,8 @@ export default defineComponent({
       /** 生成评测记录的时候,记录当前评测的谱面类型,用于评测报告默认展示的谱面类型 */
       evaluatingData.resultData.scoreData.musicType = state.musicRenderType;
       // 评测的速度,如果是选段,则选选段开头小节的速度
-      const evaluatSpeed = state.sectionStatus && state.section.length === 2 && state.section[0].measureSpeed ? state.section[0].measureSpeed * state.basePlayRate : state.speed;      
+      const evaluatSpeed = state.sectionStatus && state.section.length === 2 && state.section[0].measureSpeed ? state.section[0].measureSpeed * state.basePlayRate : state.speed;  
+      const rate = state.basePlayRate * state.originAudioPlayRate; // 播放倍率    
       const body = {
         deviceType: browser().android ? "ANDROID" : "IOS", // 设备类型
         intonation: evaluatingData.resultData.intonation, // 音准
@@ -70,6 +71,7 @@ export default defineComponent({
         recordFilePath: evaluatingData.resultData.url, // 录音文件路径
         delFlag: evaluatingData.oneselfCancleEvaluating,
         instrumentId: state.instrumentId,
+        playRate: rate
       };
       data.saveLoading = true;
       const res = await api_musicPracticeRecordSave(body);

+ 8 - 0
src/page-instrument/header-top/index.module.less

@@ -97,6 +97,14 @@
             }
         }
     }
+    
+    .blackTitle {
+        :global{
+            .van-notice-bar{
+                color: #000 !important;
+            }
+        }
+    }
 
     .hidenBack {
         opacity: 0;

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

@@ -702,7 +702,7 @@ export default defineComponent({
                         }
                       : {}
                   }
-                  class={[styles.title, "headeTopTitleBtn"]}
+                  class={[styles.title, state.isCbsView && styles.blackTitle, "headeTopTitleBtn"]}
                   onClick={() => {
                     isMusicList.value && (musicListShow.value = true);
                   }}

+ 14 - 4
src/state.ts

@@ -626,6 +626,7 @@ const autoResetPlay = () => {
   skipNotePlay(0, true);
   // 没有开启自动重播, 不是练习模式
   if (!state.setting.repeatAutoPlay) return;
+  offsetTop = 0;
   scrollViewNote();
   setTimeout(() => {
     togglePlay("play");
@@ -817,6 +818,10 @@ export const togglePlay = async (playState: "play" | "paused", isForceCLoseToast
     console.log("设置音源")
     changeSongSourceByBeat(metronomeData.disable)
   }
+  if (playState === 'play') {
+    offsetTop = 0;
+    scrollViewNote();
+  }
   // midi播放
   if (state.isAppPlay) {
     if (playState === "paused") {
@@ -1290,17 +1295,21 @@ let offsetTop = 0;
  * @param isScroll 可选: 强制滚动到顶部, 默认: false
  * @returns void
  */
-export const scrollViewNote = () => {
+export const scrollViewNote = (resetTop?: boolean) => {
   // const cursorElement = document.getElementById("cursorImg-0")!;
   const noteId = state.times[state.activeNoteIndex].id;
-  if (!noteId || state.isSingleLine) {
+  if (state.isSingleLine) {
     return;
   }
+  if (state.activeNoteIndex <= 1 || resetTop) {
+    offsetTop = 0;
+  }
   const domId = "vf" + noteId;
-  const cursorElement: any = document.querySelector(`[data-vf=${domId}]`)?.parentElement;
+  const cursorElement: any = noteId ? document.querySelector(`[data-vf=${domId}]`)?.parentElement : document.getElementById('restDot')?.parentElement;
   const musicAndSelection = document.getElementById(state.scrollContainer)!;
+  // offsetTop = musicAndSelection.scrollTop || offsetTop;
   const noteCenterOffsetTop = cursorElement ? cursorElement?.offsetTop + (cursorElement?.offsetHeight/2) : 0;
-  // console.log('滑动',cursorElement.offsetTop,offsetTop, cursorElement, )
+  // console.log('滑动',offsetTop, noteCenterOffsetTop)
   if (!cursorElement || !noteCenterOffsetTop || !musicAndSelection || offsetTop === noteCenterOffsetTop || Math.abs(offsetTop - noteCenterOffsetTop) < 30) return;
   offsetTop = noteCenterOffsetTop;
   if (offsetTop > 100) {
@@ -1671,6 +1680,7 @@ const setState = (data: any, index: number) => {
   state.isConcert = data.musicSheetType === "CONCERT" ? true : false;
   // multiTracksSelection 返回为空,默认代表全部分轨
   state.canSelectTracks = data.multiTracksSelection === "null" || data.multiTracksSelection === "" || data.multiTracksSelection === null ? [] : data.multiTracksSelection?.split(',');
+  state.canSelectTracks = state.canSelectTracks.map((item: any)=>item.trim())
   // 开启预备小节
   state.isOpenPrepare = true;
   state.extStyleConfigJson = data.extStyleConfigJson || {}

+ 3 - 1
src/view/evaluating/index.tsx

@@ -35,7 +35,7 @@ import {
   api_startDelayCheck,
   api_closeDelayCheck,
 } from "/src/helpers/communication";
-import state, { IPlayState, clearSelection, handleStopPlay, onPlay, resetPlaybackToStart, togglePlay, initSetPlayRate, resetBaseRate } from "/src/state";
+import state, { IPlayState, clearSelection, handleStopPlay, onPlay, resetPlaybackToStart, togglePlay, initSetPlayRate, resetBaseRate, scrollViewNote } from "/src/state";
 import { IPostMessage } from "/src/utils/native-message";
 import { usePageVisibility } from "@vant/use";
 import { browser } from "/src/utils";
@@ -358,6 +358,8 @@ const handleScoreResult = (res?: IPostMessage) => {
 
 /** 开始评测 */
 export const handleStartBegin = async (preTimes?: number) => {
+  // 滚动到当前小节所在区域
+  scrollViewNote(true);
   evaluatingData.needPlayTick = false;
 	if (state.isAppPlay) {
 		await api_cloudSetCurrentTime({

+ 1 - 1
src/view/music-score/index.tsx

@@ -103,7 +103,7 @@ export default defineComponent({
 				renderSingleHorizontalStaffline: state.isSingleLine ? true : false,
 				// autoGenerateMultipleRestMeasuresFromRestMeasures: state.isSingleLine ? false : true, // 连续休止小节是否合并显示
 				autoGenerateMultipleRestMeasuresFromRestMeasures: true,
-				drawLyrics: ( ((!state.accompany && !state.music ) || state.playType === 'sing') && !state.isSimplePage) ? true : false, // 演唱模式才渲染歌词,simple页面不显示歌词
+				drawLyrics: ( ((!state.accompany && !state.music ) || state.playType === 'sing' || !state.isEvxml) && !state.isSimplePage) ? true : false, // 演唱模式才渲染歌词,simple页面不显示歌词
 				// darkMode: true, // 暗黑模式
 				// pageFormat: 'A4_P',
 				// autoBeam: true,

+ 1 - 1
src/view/selection/multipleRestMeasures.tsx

@@ -15,7 +15,7 @@ export default defineComponent({
          <>
             {
                state.activeMeasureIndex == props.item.MeasureNumberXML 
-                  && <div class={styles.dotWrap}>{props.item.multipleRestMeasures}</div>
+                  && <div class={styles.dotWrap} id="restDot">{props.item.multipleRestMeasures}</div>
             }
          </>
    }

+ 2 - 2
vite.config.ts

@@ -76,8 +76,8 @@ export default defineConfig({
         // target: "https://kt.colexiu.com",
         // target: "https://test.lexiaoya.cn",
         // target: "https://kt.colexiu.com",
-        // target: "https://test.resource.colexiu.com", // 内容平台开发环境,内容平台开发,需在url链接上加上isCbs=true
-        target: "https://test.kt.colexiu.com",
+        target: "https://test.resource.colexiu.com", // 内容平台开发环境,内容平台开发,需在url链接上加上isCbs=true
+        // target: "https://test.kt.colexiu.com",
         // target: "https://mec.colexiu.com",
         changeOrigin: true,
         rewrite: (path) => path.replace(/^\/instrument/, ""),