Переглянути джерело

Merge branch 'iteration-1.0.5-节拍器'

liushengqiang 1 рік тому
батько
коміт
d3d57be300
3 змінених файлів з 134 додано та 244 видалено
  1. 31 161
      src/helpers/metronome.ts
  2. 31 83
      src/pc/home/index.tsx
  3. 72 0
      src/pc/home/noteData.ts

+ 31 - 161
src/helpers/metronome.ts

@@ -35,12 +35,13 @@ class Metronome {
 		metronomeData.activeList = [];
 	}
 	initPlayer() {
-		if (!this.source1) {
-			this.source1 = this.loadAudio1();
-		}
-		if (!this.source2) {
-			this.source2 = this.loadAudio2();
+		if (!this.source) {
+			this.source = this.loadAudio1();
 		}
+		this.source.volume(metronomeData.disable ? 0 : 1);
+		// if (!this.source2) {
+		// 	this.source2 = this.loadAudio2();
+		// }
 		metronomeData.initPlayerState = true;
 	}
 
@@ -61,20 +62,16 @@ class Metronome {
 		}
 		if (index > -1 && metronomeData.activeIndex !== index) {
 			metronomeData.activeIndex = index;
-			// console.log("播放", metronomeData.activeIndex);
-			metronomeData.activeMetro = this.getStep(activeMetro);
+			// console.log("播放", index);
+			// metronomeData.activeMetro = this.getStep(activeMetro);
 			// console.log("🚀 ~ metronomeData.activeMetro",metronomeData.activeMetro.measureNumberIndex, metronomeData.activeMetro.index)
 			this.playAudio();
-			metronomeData.isClick = false;
 			return;
 		}
-		metronomeData.isClick = false;
 	};
 	// 播放
 	playAudio = () => {
 		if (!metronomeData.initPlayerState) return;
-		this.source = metronomeData.activeMetro?.index === 0 ? this.source1 : this.source2;
-		this.source.volume(metronomeData.disable ? 0 : 1);
 		this.source.play();
 	};
 
@@ -107,76 +104,57 @@ class Metronome {
 	calculation(times: any[]) {
 		// console.log("🚀 ~ times", times);
 		// 1.统计有多少小节
-		const measures: any = {};
+		const measures: any[] = [];
 		let xmlNumber = -1;
+		let measureListIndex = -1;
 		for (let i = 0; i < times.length; i++) {
 			const note = times[i];
 			const measureNumberXML = note?.timeNote?.measureNumber;
 			// console.log("🚀 ~ note?.noteElement?.sourceMeasure", note?.noteElement?.sourceMeasure)
-			// console.log("🚀 ~ measureNumberXML", measureNumberXML, note)
+			// console.log("🚀 ~ measureNumberXML", measureNumberXML , xmlNumber)
 			// console.log("🚀 ~ measureNumberXML", note)
 			if (measureNumberXML > -1) {
-				if (measureNumberXML != xmlNumber) {
+				if (measureNumberXML != measureListIndex) {
 					const m = {
 						measureNumberXML: measureNumberXML,
 						numerator: note?.measure?.numerator || 0,
 						time: note?.timeNote?.millisecondsPerMeasure,
 						stepList: [] as number[],
-						isRestFlag: note.isRestFlag,
-						notes: [] as any[]
+						notes: [note] as any[],
 					};
-					measures[measureNumberXML] = m;
-					xmlNumber = measureNumberXML;
+					xmlNumber++;
+					measures[xmlNumber] = m;
+					measureListIndex = measureNumberXML;
 				} else {
-					measures[measureNumberXML].notes.push(note);
+					measures[xmlNumber].notes.push(note);
 				}
 			}
 		}
-		console.log(measures, measures.length);
-		return
-
-					// 2.统计小节的拍数
-					// 3.统计小节的时长, 开始时间,结束时间
-					// console.log(measureNumberXML,note.measures, times.filter((n: any) => n?.noteElement?.sourceMeasure?.measureListIndex == measureListIndex))
-					// m.stepList = calculateMetroStep(note.measures, m);
-
-		let metroList: number[] = [];
+		// console.log(measures, measures.length);
+		const 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 =  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;
+				const calp = 1 / measure.numerator;
+				// console.log("🚀 ~ measure.numerator:", measure.numerator)
+				const totalMeasureTime = measure.notes.reduce((total: number, note: any) => {
+					return total + note?.abcNote?.duration;
+				}, 0);
+				const step = Math.floor(totalMeasureTime / calp);
+				// console.log('🚀 ~ calp:', calp, 'total', totalMeasureTime, 'step', step)
+				const startTime = measure.notes[0].timeNote.milliseconds;
+				const stepTime = measure.notes[0].timeNote.millisecondsPerMeasure / measure.numerator;
+				for (let j = 0; j < step; j++) {
+					const time = stepTime * j + startTime;
 					metroList.push(time);
-					let left = "";
-					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,
-						isRestFlag: measure.isRestFlag,
-					});
 				}
 			}
 		} catch (error) {
 			console.log(error);
 		}
-		// console.log(metroList, metroMeasure);
+		// console.log(metroList, metroList.length);
 		// 5.得到所有的节拍时间
 		metronomeData.metroList = metroList;
 		metronomeData.metroMeasure = metroMeasure;
@@ -184,112 +162,4 @@ class Metronome {
 	}
 }
 
-// 计算拍子的时值
-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].svgElement;
-		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++) {
-				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 = Math.floor(totalLength / clap);
-		// console.log(`note`, item?.svgElement?.attrs?.el,notes.length,{noteLength, exceedStep,clap}, m.measureNumberXML)
-		if (exceedStep >= 1) {
-			totalLength -= clap;
-			// 一拍
-			const measure_bbox = item?.svgElement?.attrs?.el?.parentElement?.parentElement?.getBoundingClientRect?.() || { x: 0 };
-			if (notes.length > 0) {
-				let bbox = notes[0]?.svgElement?.attrs?.el?.getBoundingClientRect?.() || { x: 0 };
-				let x: any = bbox.x - measure_bbox.x;
-				if (notes[0]._noteLength / clap >= 1) {
-					const nextNote = arr[notes[0].index + 1]?.svgElement?.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]?.svgElement?.attrs?.el, arr[notes[0].index + 1]?.svgElement?.attrs?.el, bbox.x - nextNote.x, stepWidth, m.measureNumberXML);
-				}
-				// console.log(`一拍`, notes[0]?.svgElement?.attrs?.el, m.measureNumberXML, notes[0]._noteLength , clap, 'aa')
-				stepList.push(x);
-			} else {
-				let bbox = item?.svgElement?.attrs?.el?.getBoundingClientRect?.() || { x: 0 };
-				let x: any = bbox.x - measure_bbox.x;
-				// console.log(`一拍`, item?.svgElement?.attrs?.el, m.measureNumberXML)
-				stepList.push(x);
-			}
-			notes = [];
-			let bbox = item?.svgElement?.attrs?.el?.getBoundingClientRect?.() || { x: 0 };
-			let x: any = bbox.x - measure_bbox.x;
-			let stepWidth = 0;
-			if (exceedStep > 1) {
-				// 二拍以上
-				const nextNote = arr[i + 1]?.svgElement?.attrs?.el?.getBoundingClientRect?.() || { x: measure_bbox.right } || { x: 0 };
-				stepWidth = Math.abs(bbox.x - nextNote.x) / exceedStep;
-				// console.log("二拍以上 ~ nextNote:",bbox.x , nextNote.x,stepWidth, item?.svgElement?.attrs?.el,arr[i + 1]?.svgElement?.attrs?.el, exceedStep);
-			}
-
-			for (let j = 1; j < exceedStep; j++) {
-				totalLength -= clap;
-				// console.log(`超一拍`,item?.svgElement?.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);
-	return stepList;
-}
-
 export default Metronome;

+ 31 - 83
src/pc/home/index.tsx

@@ -50,86 +50,13 @@ import TheSetting from "./component/the-setting";
 import { useRoute } from "vue-router";
 import { api_musicSheetCreationDetail, api_musicSheetCreationUpdate } from "../api";
 import instrumentsNames from "/src/constant/instrmentsNames.json";
-import { ALL_NOTES, NOTE_DOT } from "./noteData";
+import { ALL_NOTES, ALL_Pitches } from "./noteData";
 import { Close } from "@vicons/ionicons5";
 import { UseDraggable } from "@vueuse/components";
 import { getQuery } from "/src/utils/queryString";
 import Metronome, { metronomeData } from "/src/helpers/metronome";
 import cleanDeep from "clean-deep";
 
-const allPitches = [
-	"C,,,,",
-	"D,,,,",
-	"E,,,,",
-	"F,,,,",
-	"G,,,,",
-	"A,,,,",
-	"B,,,,",
-	"C,,,",
-	"D,,,",
-	"E,,,",
-	"F,,,",
-	"G,,,",
-	"A,,,",
-	"B,,,",
-	"C,,",
-	"D,,",
-	"E,,",
-	"F,,",
-	"G,,",
-	"A,,",
-	"B,,",
-	"C,",
-	"D,",
-	"E,",
-	"F,",
-	"G,",
-	"A,",
-	"B,",
-	"C",
-	"D",
-	"E",
-	"F",
-	"G",
-	"A",
-	"B",
-	"c",
-	"d",
-	"e",
-	"f",
-	"g",
-	"a",
-	"b",
-	"c'",
-	"d'",
-	"e'",
-	"f'",
-	"g'",
-	"a'",
-	"b'",
-	"c''",
-	"d''",
-	"e''",
-	"f''",
-	"g''",
-	"a''",
-	"b''",
-	"c'''",
-	"d'''",
-	"e'''",
-	"f'''",
-	"g'''",
-	"a'''",
-	"b'''",
-	"c''''",
-	"d''''",
-	"e''''",
-	"f''''",
-	"g''''",
-	"a''''",
-	"b''''",
-];
-
 export const initMusic = (total: number): IMeasure[] => {
 	return new Array(total).fill(0).map((item, index) => {
 		return {
@@ -164,9 +91,9 @@ export const initMusic = (total: number): IMeasure[] => {
 };
 
 function moveNote(note: string, step: number) {
-	var x = allPitches.indexOf(note);
+	var x = ALL_Pitches.indexOf(note);
 	if (x >= 0) {
-		const _note = allPitches[x - step];
+		const _note = ALL_Pitches[x - step];
 		return _note ? _note : note;
 	}
 	return note;
@@ -378,7 +305,10 @@ export default defineComponent({
 				svg?.appendChild(cursor);
 			},
 			onBeat: function (beatNumber: any, totalBeats: any, totalTime: any) {
-				// console.log("🚀 ~ beatNumber:", beatNumber, totalBeats, totalTime, abcData.visualObj.getBeatLength());
+				if (!data.playState) return;
+				// console.log("🚀 ~ beatNumber:", (beatNumber / totalBeats) * totalTime);
+				const time = (beatNumber / totalBeats) * totalTime;
+				metronomeData.metro.sound(time);
 			},
 			onEvent: (ev: any) => {
 				// console.log("🚀 ~ ev:", ev);
@@ -536,7 +466,12 @@ export default defineComponent({
 					renderSvg();
 					resetMidi(data.drawCount > 0 ? true : false);
 					renderBoxRect();
-					// productMetronomeData();
+					try {
+						productMetronomeData();
+					} catch (error) {
+						console.log("🚀 ~ error:", error);
+					}
+
 					resolve(1);
 
 					textAreaRef.value && (textAreaRef.value.value = data.music);
@@ -549,6 +484,7 @@ export default defineComponent({
 		const productMetronomeData = () => {
 			const times = new ABCJS.TimingCallbacks(abcData.visualObj);
 			const list: any[] = [];
+			let meter = abcData.abc.meter || "";
 			// length - 1是为了去除最后一个空的结束事件
 			for (let i = 0; i < times.noteTimings.length - 1; i++) {
 				const timeNote = times.noteTimings[i];
@@ -557,7 +493,10 @@ export default defineComponent({
 				indexStr = indexStr.split(".").map((n: string) => Number(n));
 				if (indexStr.length === 2) {
 					const measure = abcData.abc.measures[indexStr[0]];
-					const meter = measure.meter ?? abcData.abc.meter;
+					// 如果小节里面有拍号,后面一直延用这个拍号,以此类推, ps: 下个版本
+					// if (measure.meter){
+					// 	meter = measure.meter;
+					// }
 					const reg = new RegExp(/M:(\d+)\/\d+/);
 					const numerator = Number(meter.match(reg)?.[1]);
 					// console.log("🚀 ~ reg:", meter.match(reg)?.[1], abcData.abc.meter)
@@ -574,8 +513,11 @@ export default defineComponent({
 				}
 			}
 			console.log("abcData.abc.measures", list);
-			metronomeData.metro = new Metronome();
+			if (!metronomeData.metro) {
+				metronomeData.metro = new Metronome();
+			}
 			try {
+				metronomeData.activeIndex = -1;
 				metronomeData.metro.init(list);
 			} catch (error) {
 				console.log("🚀 ~ 生成节拍器数据错误:", error);
@@ -811,7 +753,7 @@ export default defineComponent({
 
 			// 拍号
 			if (type === "meter") {
-				if (data.active) {
+				if (data.active && data.active.measureIndex !== 0) {
 					if (!activeNote) return;
 					const measure = abcData.abc.measures[data.active.measureIndex];
 					measure.meter = `[${value}]`;
@@ -2043,8 +1985,14 @@ export default defineComponent({
 								<div>选段</div>
 							</div>
 
-							<div class={[styles.topBtn, styles.btnDisabled]}>
-								<div class={styles.btnImg}>
+							<div
+								class={[styles.topBtn]}
+								onClick={() => {
+									metronomeData.disable = !metronomeData.disable;
+									metronomeData.metro?.initPlayer();
+								}}
+							>
+								<div class={[styles.btnImg, !metronomeData.disable && styles.btnImgActive]}>
 									<img class={styles.topBtnIcon} src={getImage("icon_23.png")} />
 								</div>
 								<div>节拍器</div>

+ 72 - 0
src/pc/home/noteData.ts

@@ -28,6 +28,78 @@ export const ALL_NOTES = () => {
 	// console.log(notes);
 	return str;
 };
+export const ALL_Pitches = [
+	"C,,,,",
+	"D,,,,",
+	"E,,,,",
+	"F,,,,",
+	"G,,,,",
+	"A,,,,",
+	"B,,,,",
+	"C,,,",
+	"D,,,",
+	"E,,,",
+	"F,,,",
+	"G,,,",
+	"A,,,",
+	"B,,,",
+	"C,,",
+	"D,,",
+	"E,,",
+	"F,,",
+	"G,,",
+	"A,,",
+	"B,,",
+	"C,",
+	"D,",
+	"E,",
+	"F,",
+	"G,",
+	"A,",
+	"B,",
+	"C",
+	"D",
+	"E",
+	"F",
+	"G",
+	"A",
+	"B",
+	"c",
+	"d",
+	"e",
+	"f",
+	"g",
+	"a",
+	"b",
+	"c'",
+	"d'",
+	"e'",
+	"f'",
+	"g'",
+	"a'",
+	"b'",
+	"c''",
+	"d''",
+	"e''",
+	"f''",
+	"g''",
+	"a''",
+	"b''",
+	"c'''",
+	"d'''",
+	"e'''",
+	"f'''",
+	"g'''",
+	"a'''",
+	"b'''",
+	"c''''",
+	"d''''",
+	"e''''",
+	"f''''",
+	"g''''",
+	"a''''",
+	"b''''",
+];
 export const ABC_NOTE_DATA = [
 	"C,,,,",
 	["^C,,,,", "_D,,,,"],