فهرست منبع

输入选段播放

liushengqiang 1 سال پیش
والد
کامیت
6de333310d
2فایلهای تغییر یافته به همراه156 افزوده شده و 78 حذف شده
  1. 14 0
      src/pc/home/index.module.less
  2. 142 78
      src/pc/home/index.tsx

+ 14 - 0
src/pc/home/index.module.less

@@ -268,6 +268,7 @@
 }
 
 .selectMearesBox {
+    width: 286px;
     position: fixed;
     padding: 12px !important;
     background: #FFFFFF;
@@ -275,6 +276,19 @@
     border-radius: 16px;
     border: 1px solid #F5F5F7;
     cursor: move;
+    .mearesInput{
+        display: flex;
+        align-items: center;
+        flex:1;
+        border: 1px solid rgb(224, 224, 230);
+        border-radius: 4px;
+        :global{
+            .n-input__placeholder,
+            .n-input__input-el{
+                text-align: center;
+            }
+        }
+    }
 }
 .selectMearesHidden{
     display: none;

+ 142 - 78
src/pc/home/index.tsx

@@ -208,9 +208,16 @@ export default defineComponent({
 			isClickNote: false,
 			/** 音符类型 */
 			noteType: "",
-			selectMeasure: {
-				start: "",
-				end: "",
+			/** 选择小节范围 */
+			selectMeasures: {
+				state: false,
+				x: 0,
+				y: 0,
+				start: 1,
+				startNote: null as any,
+				end: 0,
+				endNote: null as any,
+				max: 30,
 			},
 			slide: ["note", "meter", "dynamics"],
 
@@ -223,11 +230,6 @@ export default defineComponent({
 			loadingAudioSrouce: false, // 加载音频资源
 			/** 移调类型 */
 			moveKeyType: "inset" as "inset" | "up" | "down", // 移调类型
-			selectMearesDrag: {
-				state: false,
-				x: 0,
-				y: 0,
-			}, // 选择小节拖拽状态
 		});
 		const noteTypes = ABC_DATA.types.map((item) => item.value).filter(Boolean);
 		const accidentals = ABC_DATA.accidentals.map((item) => item.value).filter(Boolean);
@@ -282,7 +284,7 @@ export default defineComponent({
 				const totalTime = (abcData.synthControl as any).visualObj.getTotalTime();
 				if (totalTime) {
 					const progress = (data.active as any).currentTrackMilliseconds / 1000 / totalTime;
-					// console.log("🚀 ~ data.active:", data.active, progress);
+					// console.log("🚀 ~ data.active:", progress);
 					(abcData.synthControl as any).seek(progress);
 				}
 			}
@@ -344,27 +346,6 @@ export default defineComponent({
 			}
 		};
 
-		/**
-		 * 分词
-		 * @param str 字符串
-		 * @returns
-		 * @description
-		 */
-		const tokenize = (str: string) => {
-			const arr = str.split(/(!.+?!|".+?")/);
-			let output: string[] = [];
-			for (let i = 0; i < arr.length; i++) {
-				const token = arr[i];
-				if (token.length > 0) {
-					if (token[0] !== '"' && token[0] !== "!") {
-						const arr2 = arr[i].split(/([A-Ga-gz][,']*)/);
-						output = output.concat(arr2);
-					} else output.push(token);
-				}
-			}
-			return output;
-		};
-
 		const hideCursor = () => {
 			const cursor = document.querySelector("#paper svg .ABCJS-cursor");
 			if (cursor) {
@@ -376,13 +357,7 @@ export default defineComponent({
 		};
 
 		const cursorControl = {
-			// self.onReady = function () {
-			// 	var downloadLink = document.querySelector(".download");
-			// 	downloadLink.addEventListener("click", download);
-			// 	downloadLink.setAttribute("style", "");
-			// 	var clickEl = document.querySelector(".click-explanation");
-			// 	clickEl.setAttribute("style", "");
-			// };
+			onReady: function () {},
 			onStart: function () {
 				console.log("开始");
 				data.playState = true;
@@ -395,12 +370,28 @@ export default defineComponent({
 				cursor.setAttributeNS(null, "y2", "0");
 				svg?.appendChild(cursor);
 			},
-			// self.beatSubdivisions = 2;
 			onBeat: function (beatNumber: any, totalBeats: any, totalTime: any) {},
 			onEvent: (ev: any) => {
+				// console.log("🚀 ~ ev:", ev);
 				if (!data.playState) return;
 				if (ev.measureStart && ev.left === null) return; // this was the second part of a tie across a measure line. Just ignore it.
-
+				if (popup.selectMearesShow) {
+					const startTime = data.selectMeasures.startNote?.currentTrackMilliseconds || 0;
+					const endNote: any = data.selectMeasures.endNote ? data.selectMeasures.endNote : null;
+					// console.log("🚀 ~ endNote:", ev.milliseconds , endNote.currentTrackMilliseconds)
+					if (
+						ev.milliseconds < startTime ||
+						(endNote && ev.milliseconds > endNote.currentTrackMilliseconds)
+					) {
+						const totalTime = (abcData.synthControl as any).visualObj.getTotalTime();
+						if (totalTime) {
+							const progress = startTime / 1000 / totalTime;
+							nextTick(() => {
+								(abcData.synthControl as any).seek(progress);
+							});
+						}
+					}
+				}
 				var cursor = document.querySelector("#paper svg .ABCJS-cursor");
 				if (cursor) {
 					cursor.setAttribute("x1", ev.left + ev.width / 2);
@@ -447,7 +438,7 @@ export default defineComponent({
 						})
 						.then(function (response) {
 							data.loadingAudioSrouce = false;
-							// console.log("Audio successfully loaded.");
+							// console.log("Audio successfully loaded.", {...abcData.synthControl});
 							// console.log("🚀 ~ abcData.synthControl:", abcData.synthControl);
 						})
 						.catch((err) => {
@@ -457,7 +448,6 @@ export default defineComponent({
 		};
 
 		const togglePlay = (type: "play" | "pause" | "reset") => {
-			console.log("🚀 ~ abcData.synthControl:", abcData.synthControl);
 			if (type === "play") {
 				abcData.synthControl.play();
 				data.playState = true;
@@ -468,6 +458,7 @@ export default defineComponent({
 			} else {
 				abcData.synthControl.restart();
 			}
+			// console.log("🚀 ~ abcData.synthControl:", abcData.synthControl.timer.noteTimings);
 		};
 
 		const renderSvg = () => {
@@ -483,14 +474,18 @@ export default defineComponent({
 		const renderBoxRect = () => {
 			const svg = document.querySelector("#paper svg");
 			const padding = 4;
+			let measureNumber = 0;
 			for (let i = 0; i < abcData.visualObj.lines.length; i++) {
 				const line = abcData.visualObj.lines[i];
-				console.log("🚀 ~ line:", line);
+				// console.log("🚀 ~ line:", line);
 				for (let j = 0; j < line.staff.length; j++) {
 					const staff = line.staff[j];
 					const voices = [...staff.voices].flat();
 					for (let l = 0; l < voices.length; l++) {
 						const item = voices[l];
+						if (item.el_type === "bar") {
+							measureNumber++;
+						}
 						// console.log(item.el_type);
 						if (["note", "keySignature", "clef", "timeSignature"].includes(item.el_type)) {
 							const box = item.abselem.elemset?.[0]?.getBBox?.() || null;
@@ -511,21 +506,26 @@ export default defineComponent({
 					}
 				}
 			}
+			console.log(measureNumber);
+      data.selectMeasures.max = measureNumber;
 		};
 
 		/**
 		 * @param isProduct 是否是生成曲谱
 		 */
-		const handleResetRender = (isProduct = true) => {
+		const handleResetRender = () => {
 			return new Promise((resolve) => {
 				nextTick(() => {
-					data.music = isProduct ? renderMeasures(abcData.abc) : data.music;
+					data.music = renderMeasures(abcData.abc);
 					renderSvg();
 					resetMidi(data.drawCount > 0 ? true : false);
 					renderBoxRect();
 					resolve(1);
 					textAreaRef.value && (textAreaRef.value.value = data.music);
 					data.drawCount++;
+
+					const times = new ABCJS.TimingCallbacks(abcData.visualObj);
+					console.log("🚀 ~ times:", times)
 				});
 			});
 		};
@@ -1154,6 +1154,13 @@ export default defineComponent({
 			};
 			abcData.synthControl.restart();
 			// formateAbc(abcData.visualObj);
+			const selectMearesBtn = document.querySelector("#selectMearesBtn");
+			if (selectMearesBtn) {
+				const rect = selectMearesBtn.getBoundingClientRect();
+				data.selectMeasures.x = document.body.clientWidth - 320;
+				data.selectMeasures.y = rect.top + 70;
+				data.selectMeasures.state = true;
+			}
 		});
 		onUnmounted(() => {
 			document.removeEventListener("keyup", handleKeyUp);
@@ -1309,14 +1316,49 @@ export default defineComponent({
 					console.log("🚀 ~ abc:", abc);
 					abc = (window as any).vertaal(abc, { p: "f", t: 1, u: 0, v: 3, mnum: 0 });
 					console.log(abc);
-					data.music = abc[0];
-					handleResetRender(false);
+					// data.music = abc[0];
+					// handleResetRender(false);
 				};
 				reader.readAsText(file);
 			};
 			input.click();
 		};
 
+		/** 设置选段小节 */
+		const handleSetSelectMeares = (index: number | null, type: "start" | "end") => {
+			console.log("🚀 ~ index:", index);
+			if (type === "start") {
+				const note = index ? useIndexGetNote(`${index - 1}.0`) : null;
+				// console.log("🚀 ~ note:", note);
+				data.selectMeasures.start = index ? index - 1 : 0;
+				data.selectMeasures.startNote = note;
+				if (
+					data.selectMeasures.start &&
+					data.selectMeasures.end &&
+					data.selectMeasures.end < data.selectMeasures.start
+				) {
+					data.selectMeasures.end = 0;
+					data.selectMeasures.endNote = null;
+				}
+			} else {
+				const note = index
+					? useIndexGetNote(`${index - 1}.${abcData.abc.measures[index - 1]?.notes.length - 1}`)
+					: null;
+				// console.log("🚀 ~ note:", note);
+				data.selectMeasures.end = index ? index - 1 : 0;
+				data.selectMeasures.endNote = note;
+				if (
+					data.selectMeasures.start &&
+					data.selectMeasures.end &&
+					data.selectMeasures.start > data.selectMeasures.end
+				) {
+					// console.log(data.selectMeasures.start, data.selectMeasures.end);
+					data.selectMeasures.start = 0;
+					data.selectMeasures.startNote = null;
+				}
+			}
+		};
+
 		return () => (
 			<div class={styles.container}>
 				<div class={styles.containerTop} onKeyup={(e: Event) => e.stopPropagation()}>
@@ -1877,6 +1919,7 @@ export default defineComponent({
 							<div>{data.playState ? "暂停" : "播放"}</div>
 						</div>
 						<div
+							id="selectMearesBtn"
 							class={[styles.topBtn]}
 							onClick={() => (popup.selectMearesShow = !popup.selectMearesShow)}
 						>
@@ -2112,38 +2155,59 @@ export default defineComponent({
 
 				<TheSetting v-model:show={popup.settingShow} />
 
-				{/* <UseDraggable
-					initialValue={{ x: 320, y: 60 }}
-					class={[styles.selectMearesBox, !popup.selectMearesShow && styles.selectMearesHidden]}
-				>
-					<NSpace justify="space-between">
-						<div class={styles.btnLineTitle}>输入小节范围</div>
-						<NButton circle quaternary size="small" onClick={() => (popup.selectMearesShow = false)}>
-							<NIcon size={16} component={<Close />} />
-						</NButton>
-					</NSpace>
-					<NSpace align="center">
-						<div style={{ width: "200px" }}>
-							<NInput pair placeholder={["开始小节", "结束小节"]}></NInput>
-						</div>
-						<div class={styles.topBtn}>
-							<NSpin show={data.loadingAudioSrouce} size="small">
-								<div class={styles.btnImg} onClick={() => togglePlay(data.playState ? "pause" : "play")}>
-									<img
-										style={{ display: data.playState ? "" : "none" }}
-										class={styles.topBtnIcon}
-										src={getImage("icon_21_1.png")}
-									/>
-									<img
-										style={{ display: data.playState ? "none" : "" }}
-										class={styles.topBtnIcon}
-										src={getImage("icon_21.png")}
-									/>
-								</div>
-							</NSpin>
-						</div>
-					</NSpace>
-				</UseDraggable> */}
+				{data.selectMeasures.state && (
+					<UseDraggable
+						initialValue={{ x: data.selectMeasures.x, y: data.selectMeasures.y }}
+						class={[styles.selectMearesBox, !popup.selectMearesShow && styles.selectMearesHidden]}
+					>
+						<NSpace justify="space-between">
+							<div class={styles.btnLineTitle}>输入小节范围</div>
+							<NButton circle quaternary size="small" onClick={() => (popup.selectMearesShow = false)}>
+								<NIcon size={16} component={<Close />} />
+							</NButton>
+						</NSpace>
+						<NSpace align="center" wrap={false} wrapItem={false}>
+							<div class={styles.mearesInput}>
+								<NInputNumber
+									min={1}
+									max={data.selectMeasures.max}
+									bordered={false}
+									placeholder="开始小节"
+									showButton={false}
+									onUpdate:value={(val) => handleSetSelectMeares(val, "start")}
+								></NInputNumber>
+								-
+								<NInputNumber
+									min={data.selectMeasures.start}
+									max={data.selectMeasures.max}
+									bordered={false}
+									placeholder="结束小节"
+									showButton={false}
+									onUpdate:value={(val) => handleSetSelectMeares(val, "end")}
+								></NInputNumber>
+							</div>
+							<div class={styles.topBtn}>
+								<NSpin show={data.loadingAudioSrouce} size="small">
+									<div
+										class={styles.btnImg}
+										onClick={() => togglePlay(data.playState ? "pause" : "play")}
+									>
+										<img
+											style={{ display: data.playState ? "" : "none" }}
+											class={styles.topBtnIcon}
+											src={getImage("icon_21_1.png")}
+										/>
+										<img
+											style={{ display: data.playState ? "none" : "" }}
+											class={styles.topBtnIcon}
+											src={getImage("icon_21.png")}
+										/>
+									</div>
+								</NSpin>
+							</div>
+						</NSpace>
+					</UseDraggable>
+				)}
 			</div>
 		);
 	},