Browse Source

时值优化

黄琪勇 5 months ago
parent
commit
71ad4864e5
2 changed files with 90 additions and 25 deletions
  1. 52 21
      src/helpers/formateMusic.ts
  2. 38 4
      src/helpers/metronome.ts

+ 52 - 21
src/helpers/formateMusic.ts

@@ -1216,6 +1216,7 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 			// console.log('si',si,i)
 			// console.log(note.sourceMeasure.MeasureNumberXML,note,svgElement, NoteRealValue, measureLength)
 			if (allNotes.length && allNotes[allNotes.length - 1].relativeTime === relativeTime) {
+				i++
 				continue;
 			}
 			// console.log(iterator.currentMeasure)
@@ -1286,39 +1287,55 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 			/**
 			 * evxml的曲子,如果曲谱xml中带有times信息,则音符时值优先取times中的值
 			 * 曲子:1795013295024062466(春暖花开),如果音符有times信息,休止符没有times信息,此种规则是认为休止符不参与时值计算的,需要过滤掉该休止符
-			 * TODO:需要考虑唱名怎么处理,唱名是xml有多少个音符,就需要唱多少个,不能剔除
 			 */
-			if (state.isEvxml && note.isRestFlag && note?.noteTimeInfo?.length === 0 && state.xmlHasTimes ) {
-				const idx = _notes.findIndex(item=>item.note === note);
-				let nextNoteTimes = _notes[idx+1]?.note?.noteTimeInfo?.[0]?.begin*1000 
-				let preNoteTImes = _notes[idx-1]?.note?.noteTimeInfo?.[0]?.end*1000
-				// 当下一个音符也没有时间的时候,再往下一个找
-				if(!nextNoteTimes && nextNoteTimes!==0){
-					let nextIndex = idx + 2
-					while(!nextNoteTimes && nextIndex<_notes.length){
-						nextNoteTimes = _notes[nextIndex]?.note?.noteTimeInfo?.[0]?.begin*1000
-						nextIndex ++
-					}
-					// 当最后音符就是没有打时间的休止小节,可能nextNoteTimes时间找不到,目前没有处理
+			let evNoteStartTime = 0, evNoteEndTime = 0;
+			if (state.isEvxml && note?.noteTimeInfo?.length === 0 && state.xmlHasTimes ) {
+				// 找出这个音符前面音符的结束时间
+				let preNoteTImes = allNotes[allNotes.length - 1]?.endtime*1000
+				if(!preNoteTImes){
+					preNoteTImes = Math.max(fixtime - noteLength, 0)*1000 //如果前一个音符没有结束时间,证明这个音符是第一个音符没有打时间,所以往前奏里面找补
 				}
-				if(!preNoteTImes && preNoteTImes!==0){
-					let preIndex = idx - 2
-					while(!preNoteTImes && preIndex>-1){
-						preNoteTImes = _notes[preIndex]?.note?.noteTimeInfo?.[0]?.end*1000
-						preIndex --
+				// 找出这个音符后面音符的开始时间
+				let nextI = i
+				let nextNoteTimes
+				// 多个连续的没有打时间的音符 需要平分时值
+				const notesRatio = []
+				while (!nextNoteTimes && nextI<_notes.length) {
+					notesRatio.push(_notes[nextI].note.length.realValue)
+					nextI++
+					if(_notes[nextI]?.note){ // 有可能_notes里面没有这个音符
+						nextNoteTimes = fliterNotesTime(_notes[nextI].note, preNoteTImes)
 					}
-					// 当没有找到preNoteTImes的时候 赋值为0 (当第一个音符就是没有打时间的休止小节会出现这种情况)
-					preNoteTImes || (preNoteTImes = 0)
 				}
+				// 当最后音符就是没有打时间的音符,可能nextNoteTimes时间找不到时候取上个音符的结束时间加上这个音符的时间
+				if(!nextNoteTimes){
+					nextNoteTimes = preNoteTImes + noteLength*1000
+				}
+				// 判断有没有首位连续 首位连续的时候删掉这个音符
 				const allowRange = Math.abs(nextNoteTimes - preNoteTImes)< 10;
 				if (allowRange) {
 					note.maxNoteNum = note.maxNoteNum - 1;
 					// 唱名时间补齐,当删除这个音符的时候,上个音符的持续时间要加上这个音符的时间
 					allNotes[allNotes.length - 1].noteLengthTime += noteLength
+					i++
 					continue;
+				}else{
+					// 当多个连续的休止符没有打时间的时候 根据音符平均分配
+					if(notesRatio.length > 1){
+						const sum = notesRatio.reduce((acc:number, curr:number) => acc + curr, 0)
+						nextNoteTimes = (nextNoteTimes - preNoteTImes) * notesRatio[0] / sum + preNoteTImes
+					}
+					evNoteEndTime = nextNoteTimes/1000
+					evNoteStartTime = preNoteTImes/1000
+					// 当这个音符计算出来的时值大于本身这个音符的时值时候,取这个音符的长度,防止有前奏和间奏的时候,计算音符持续时长过长
+					if(evNoteEndTime - evNoteStartTime > noteLength){
+						evNoteEndTime = evNoteStartTime + noteLength
+					}
+					if (evNoteStartTime) {
+						relativeTime = evNoteStartTime - fixtime
+					}
 				}
 			}
-			let evNoteStartTime = 0, evNoteEndTime = 0;
 			if (state.isEvxml && note?.noteTimeInfo?.length ) {
 				let idx = noteIds.filter((item: any) => item === svgElement?.attrs.id)?.length || 0;
 				// 如果是合并的小节的休止符
@@ -1745,4 +1762,18 @@ export const compatibleXmlPitchVoice = (xmlParse: any) => {
 		}
 		(window as any).xmlNeedAdjustVoice = xmlNeedAdjustVoice
 	}
+}
+
+// 筛选出这个音符中的值(比前一个音符时值大的值)
+function fliterNotesTime(note:any, preTime:number):undefined|number {
+	// 音符可能涉及多遍歌词,所以这里要找到对应的times
+	if(note?.noteTimeInfo?.length){
+		const timeObj = note?.noteTimeInfo.find((value:any) => {
+			const beginTime = value?.begin*1000 || 0
+			return beginTime > preTime || Math.abs(beginTime - preTime)< 10  //差值在10毫秒之内
+		})
+		return timeObj?.begin*1000
+	}else{
+		return undefined
+	}
 }

+ 38 - 4
src/helpers/metronome.ts

@@ -277,27 +277,61 @@ class Metronome {
 		// 1.统计有多少小节
 		const measures: any[] = [];
 		let xmlNumber = -1;
+		let isDiff = false //弱起时间不够补的时候
 		for (let i = 0; i < times.length; i++) {
 			const note = times[i];
-			const measureNumberXML = note?.noteElement?.sourceMeasure?.MeasureNumberXML;
+			const measureNumberXML = note.MeasureNumberXML;
 			// console.log("🚀 ~ note?.noteElement?.sourceMeasure", note?.noteElement?.sourceMeasure)
 			// console.log("🚀 ~ measureNumberXML", measureNumberXML, note)
 			// console.log("🚀 ~ measureNumberXML", note)
-			const measureListIndex = note?.noteElement?.sourceMeasure?.measureListIndex;
+			const measureListIndex = measureNumberXML - 1;
 			if (measureNumberXML > -1) {
 				if (measureNumberXML != xmlNumber) {
 					// 弱起的时候 根据音符结尾时间减去音符开头时间,得到的不是正常小节的时间,然后平均分配节拍之后,当前节拍间隔会非常短 这里弱起取整个小节的时间
+					// 当小节时间 减去音符时间,前奏没有预留时间时候,从歌词开始唱的那里开始响节拍器
 					let startTime = note.measures[0].time
 					if(i === 0 && note.measures[0].difftime>0){
 						startTime = note.measures[note.measures.length - 1].endtime - note.measures[0].measureLength
+						if(startTime < 0){
+							isDiff = true
+						}
+					}
+					if(isDiff) {
+						// 当前小节有歌词,开放弱起节拍器
+						let isLyric = false
+						let noteIndex = 0
+						while(!isLyric && noteIndex<note.measures.length){
+							isLyric = !!note.measures[noteIndex]?.formatLyricsEntries?.length
+							noteIndex ++
+						}
+						isDiff = !isLyric
+					}
+					if(isDiff){
+						xmlNumber = measureNumberXML;
+						continue
+					}
+					// 最后一小节的时间 有可能和下一小节开始时间接不上
+					const { time, endtime, noteLengthTime } = note.measures[note.measures.length - 1]
+					let nextNoteStartTime = times[note.measures[note.measures.length - 1].i + 1]?.time
+					let noteEndTime = 0
+					if(!nextNoteStartTime){
+						noteEndTime = time + noteLengthTime
+					}else{
+						if(Math.abs(nextNoteStartTime - endtime)*1000< 10){
+							// 当首位本来就是相连的
+							noteEndTime = endtime
+						}else{
+							// 当首位不相连,差值大于这个音符的持续时间的时候取这个音符的时间(防止有间奏的连起来时间太长),否则直接取后一个音符的开始时间
+							noteEndTime = nextNoteStartTime - time > noteLengthTime ? time + noteLengthTime : nextNoteStartTime
+						}	
 					}
 					const m = {
 						measureNumberXML: measureNumberXML,
 						measureNumberIndex: measureListIndex,
 						numerator: note?.noteElement?.sourceMeasure?.ActiveTimeSignature?.numerator || 0,
 						start: startTime,
-						end: note.measures[note.measures.length - 1].endtime,
-						time: note.measures[note.measures.length - 1].endtime - startTime,
+						end: noteEndTime,
+						time: noteEndTime - startTime,
 						stave_x: note?.noteElement?.sourceMeasure?.verticalMeasureList?.[0]?.stave?.x || 0,
 						end_x: note?.stave?.end_x || 0 || 0,
 						stepList: [] as number[],