Browse Source

速度逻辑同步

TIANYONG 2 months ago
parent
commit
b649fba38c

+ 31 - 9
src/helpers/formateMusic.ts

@@ -14,6 +14,8 @@ import {
 	OpenSheetMusicDisplay,
 } from "/osmd-extended/src";
 import { GradualChange, speedInfo } from "./calcSpeed";
+import { beatUnitTo, speedBeatTo } from "/src/helpers/beatConfig"
+
 const browserInfo = browser();
 dayjs.extend(duration);
 
@@ -685,11 +687,19 @@ export const formatXML = (xml: string, xmlUrl?: string): string => {
 		state.originSpeed = speeds[0] ? speeds[0] : 100;
 		state.speed = state.originSpeed;
 	}
+	// 赋值谱面速度节拍器,没有的时候 以后台传入的为准
+	const metronomeXml = xmlParse.getElementsByTagName('metronome')?.[0]
+	const beatUnit = metronomeXml?.getElementsByTagName('beat-unit')?.[0]?.textContent || ''
+	if(beatUnit){
+		const beatUnitDot = metronomeXml?.getElementsByTagName('beat-unit-dot')?.[0]
+		state.speedBeatUnit = beatUnitTo(beatUnit, !!beatUnitDot)
+	}	
 	// 如果谱面和小节都没有打速度,osmd设置的小节速度默认取后台设置的速度
 	if (speeds.length === 0) {
 		;(window as any).baseMeasureSpeed = state.originSpeed
 	} else {
-		state.originAudioPlayRate = speeds[0] / state.originSpeed
+		// 当前谱面的速度转为4分音符速度 因为我们速度比例转为4分音符了
+		state.originAudioPlayRate = speedBeatTo({unit:state.speedBeatUnit, speed:speeds[0]}, "1/4") / state.originSpeed
 	}
 	console.log('是否是变速的曲子:',hasVaryingSpeed,speeds)
 
@@ -852,6 +862,7 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 	let preNoteEndTime = 0; // 上一个音符的结束时间
 
 	let preNoteMeasureNumber: any = null; // 上一个小节的number值
+	let currentRealTempo: any = {}; // 当前小节的速度与拍号信息
 
 	const _notes = [] as any[];
 	if (state.gradualTimes) {
@@ -965,13 +976,20 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 			}
 			note.maxNoteNum = maxNoteNum;
 			note.trackIndex = minIndex;
+			currentRealTempo = iterator.currentMeasure.tempoExpressions.length ? iterator.currentMeasure.tempoExpressions.find((item: any) => item?.InstantaneousTempo?.isMetronomeMark)?.InstantaneousTempo || currentRealTempo : currentRealTempo;
+			const { beatUnit="quarter", dotted=false, tempoInBpm=state.originSpeed } = currentRealTempo
+			const speedBeatUnit = beatUnitTo(beatUnit, dotted)			
 			_notes.push({
 				note,
 				iterator: { ...iterator },
 				currentTime,
 				isDouble,
 				isMutileSubject,
-				measuresTempoInBPM: note?.sourceMeasure?.tempoInBPM
+				// measuresTempoInBPM: note?.sourceMeasure?.tempoInBPM,
+				// 转换成1/4拍的速度
+				measuresTempoInBPM: speedBeatTo({unit: speedBeatUnit || "1/4",speed: tempoInBpm || 0}, `1/4`),
+				speedBeatUnit, // 当前谱面小节的速度对应的是几分音符
+				currentRealTempo
 			});
 		}
 
@@ -989,7 +1007,7 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 	console.log('变速曲子',hasVaryingSpeed, _notes)
 	let noteIds: any = [];
 	// let voicesBBox: any = null;
-	for (let { note, iterator, currentTime, isDouble, isMutileSubject } of _notes) {
+	for (let { note, iterator, currentTime, isDouble, isMutileSubject, speedBeatUnit, measuresTempoInBPM } of _notes) {
 		if (note) {
 			if (preMeasureNumber != note?.sourceMeasure?.MeasureNumberXML) {
 				si = 0
@@ -1097,12 +1115,15 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 
 			let beatSpeed = 0;
 			// 速度不能为0 此处的速度应该是按照设置的速度而不是校准后的速度,否则mp3速度不对
-			if (measureSpeed !== baseSpeed && !hasVaryingSpeed) {
-				beatSpeed = baseSpeed || measureSpeed || 100
-			} else {
-				beatSpeed = (state.isSpecialBookCategory ? measureSpeed : baseSpeed) || 1;
-			}
+			// if (measureSpeed !== baseSpeed && !hasVaryingSpeed) {
+			// 	beatSpeed = baseSpeed || measureSpeed || 100
+			// } else {
+			// 	beatSpeed = (state.isSpecialBookCategory ? measureSpeed : baseSpeed) || 1;
+			// }
+			// 计算音符时值,使用转换成1/4的速度计算
+			beatSpeed = measuresTempoInBPM;
 			// let beatSpeed = measureSpeed || baseSpeed
+			beatSpeed = beatSpeed / state.originAudioPlayRate;
 			// 如果有节拍器,需要将节拍器的时间算出来
 			if (i === 0) {
 				if(state.isOpenMetronome){
@@ -1410,7 +1431,8 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 				firstVerticalMeasure: activeVerticalMeasureList[0],
 				noteLength: 1,
 				osdmContext: osmd,
-				speedbeatUnit: beatUnit,
+				// speedbeatUnit: beatUnit,
+				speedBeatUnit,
 				multipleRestMeasures: multipleRestMeasures, // 当前合并小节的索引,从1开始到当前的totalMultipleRestMeasures结束,
 				totalMultipleRestMeasures, // 当前小节总的合并小节数
 				measureSpeed,  // 小节速度

BIN
src/page-instrument/header-top/image/speed1.png


BIN
src/page-instrument/header-top/image/speed2.png


BIN
src/page-instrument/header-top/image/speed3.png


BIN
src/page-instrument/header-top/image/speed4.png


BIN
src/page-instrument/header-top/image/speed5.png


BIN
src/page-instrument/header-top/image/speed6.png


BIN
src/page-instrument/header-top/image/speed7.png


BIN
src/page-instrument/header-top/image/speed8.png


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

@@ -913,7 +913,7 @@ export default defineComponent({
                   <img style={{ display: !metronomeData.disable ? "block" : "none" }} class={styles.iconBtn} src={headImg("tickoff.png")} />
                   <span style={{ whiteSpace: "nowrap" }}>节拍</span>
                   <div class={styles.speedCon}>
-                    <img src={headImg("speed.png")} />
+                    <img src={headImg(`${state.speedIcon}.png`)} />
                     <div>{Math.floor(state.speed)}</div>
                   </div>
                 </div>

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

@@ -166,19 +166,24 @@
                 }
             }
             .speedSel{
-                margin-top: 20px;
+                margin-top: 8px;
                 padding-bottom: 18px;
                 display: flex;
                 justify-content: space-between;
+                flex-wrap: wrap;
                 & > div{
-                    padding: 6px 13px;
+                    width: 42px;
+                    height: 24px;
+                    line-height: 24px;
+                    text-align: center;
                     background: #FFFFFF;
                     border-radius: 14px;
                     font-weight: 400;
                     font-size: 13px;
                     color: rgba(0,0,0,0.6);
-                    line-height: 1;
                     cursor: pointer;
+                    margin-top: 10px;
+                    margin-right: 3px;
                     &:active{
                         background: linear-gradient( 131deg, #44CAFF 0%, #259CFE 100%);
                         color: #fff;

+ 3 - 5
src/page-instrument/header-top/speed/index.tsx

@@ -137,11 +137,9 @@ export default defineComponent({
 						</div>
 						<div class={[styles.speedSel, (workData.trainingType === "PRACTICE" || workData.trainingType === "EVALUATION") && styles.disableSpend]}>
 							<div onClick={resetCurrentSpeed}>原速</div>
-							<div onClick={()=>{ speed.value = 70 }}>70</div>
-							<div onClick={()=>{ speed.value = 80 }}>80</div>
-							<div onClick={()=>{ speed.value = 90 }}>90</div>
-							<div onClick={()=>{ speed.value = 100 }}>100</div>
-							<div onClick={()=>{ speed.value = 110 }}>110</div>
+							{[60,70,80,90,100,110,120,130,140,150,160].map((item) => (
+								<div onClick={()=>{ speed.value = item }}>{item}</div>
+							))}
 						</div>
 						{
 							state.isMixBeat && 

+ 10 - 3
src/state.ts

@@ -22,7 +22,7 @@ import { musicScoreRef, headerColumnHide } from "/src/page-instrument/view-detai
 import { headTopData } from "/src/page-instrument/header-top/index";
 import { api_lessonTrainingTrainingStudentDetail } from "/src/page-instrument/api"
 import { undoData, moveData } from "/src/view/plugins/move-music-score"
-import { speedBeatTo } from "/src/helpers/beatConfig"
+import { speedBeatTo, unitImgs } from "/src/helpers/beatConfig"
 
 const query: any = getQuery();
 
@@ -588,6 +588,8 @@ const state = reactive({
   firstMeasureNumber: 1,
   /** 是否是自动重播,练习模式开启自动重播时,播放前不需要再次计算播放倍率了,还是按照上次的播放倍率播放音频 */
   isAutoRePlay: false,
+  /** 右上角速度图标,根据当前小节的速度是几分音符的动态变化 */
+  speedIcon: 'speed3', // 默认取1/4拍的图片
 });
 const browserInfo = browser();
 let offset_duration = 0;
@@ -800,7 +802,7 @@ export const skipNotePlay = async (itemIndex: number, isStart = false) => {
   if (item) {
     // 非选段模式,点击音符,动态设置右下角的速度
     if (item.measureSpeed && state.section.length < 2) {
-      state.speed = Math.floor(state.basePlayRate * item.measureSpeed)
+      state.speed = state.basePlayRate * 10000 * item.measureSpeed / 10000
     }
     setAudioCurrentTime(itemTime, itemIndex);
     // 一行谱,点击音符,或者播放完成,需要跳转音符位置
@@ -1156,7 +1158,8 @@ export const handleSetSpeed = (speed: number) => {
   // setStorageSpeed(state.examSongId, speed);
   state.speed = speed;
   // 当前的音符
-  const currentItem: any = (state.sectionStatus && state.section.length === 2) ? state.sectionFirst || state.section[0] : state.times[state.activeNoteIndex];
+  // const currentItem: any = (state.sectionStatus && state.section.length === 2) ? state.sectionFirst || state.section[0] : state.times[state.activeNoteIndex];
+  const currentItem: any = state.times[state.activeNoteIndex];
   state.basePlayRate = currentItem?.measureSpeed ? state.speed / currentItem.measureSpeed : state.speed / state.originSpeed;
   const actualRate = state.originAudioPlayRate * state.basePlayRate;
   console.log('速度设置',speed,'小节计算的倍率',state.basePlayRate,'实际播放倍率',actualRate)
@@ -2040,6 +2043,10 @@ function getNeedReduceMultipleRestNum(currMeasureIndex: number) {
 watch(
   () => state.activeMeasureIndex,
   () => {
+    // 监听音符小节的变化,取对应的小节速度图片
+    const currentNote = state.times[state.activeNoteIndex]
+    state.speedIcon = unitImgs[currentNote.speedBeatUnit]
+        
     // 需要减去的合并小节数
     // const needReduceMultipleRestNum = getNeedReduceMultipleRestNum(state.activeMeasureIndex)
     // const matchMeasureNum = state.activeMeasureIndex - needReduceMultipleRestNum - 1