liushengqiang 2 vuotta sitten
vanhempi
commit
9fbe924a9c
4 muutettua tiedostoa jossa 207 lisäystä ja 106 poistoa
  1. 45 67
      public/midi/index.html
  2. 5 4
      src/state.ts
  3. 84 35
      src/view/audio-list/index.tsx
  4. 73 0
      src/view/audio-list/midiPlayer.tsx

+ 45 - 67
public/midi/index.html

@@ -35,91 +35,69 @@
     <input type="range" id="skipBtn" value="100" min="45" max="200" />
     <script type="text/javascript">
         var midiData = {
-            src: ''
+            src: '',
+            timeWarp: 1,
         }
+        var currentTime = 0
         var MIDI = window.MIDI
         MIDI.loadPlugin({
             soundfontUrl: "./soundfont/",
-            // instrument: "synth_drum",
-            onprogress: function (state, progress) {
-                console.log(1, state, progress);
-            },
-        });
-
-        // 加载midi文件
-        function handleLoadFile(src, loaded = () => { }) {
-            if (!midiData.src) {
-                midiData.src = src
+            instrument: ["acoustic_grand_piano", "synth_drum"],
+            // onprogress: function (state, progress) {
+            //     console.log(1, state, progress);
+            // },
+            onsuccess: function () {
+                console.log(MIDI)
+                window.handleRendered && window.handleRendered()
+                MIDI.setInstrument(0, MIDI.GM.byName['synth_drum'].number)
             }
-            MIDI.Player.loadFile(src, loaded)
-        }
-
-        // 播放
-        function hanldePlay() {
-            MIDI.Player.resume()
-        }
-
-        // 暂停
-        function handlePause() {
-            MIDI.Player.pause();
-        }
+        });
 
-        // 跳转
-        function skipTime(time) {
-            MIDI.Player.currentTime = time * 1000
-            MIDI.Player.resume();
+        // 播放音符
+        function playNote(note, delay) {
+            MIDI.noteOn(0, note, 100, 0);
+            MIDI.noteOff(0, note, delay);
         }
 
-        // 设置播放速度
-        function setTimeWarp(timeWarp) {
-            MIDI.Player.loadMidiFile(() => {
-                MIDI.Player.timeWarp = 1 / timeWarp
-                MIDI.Player.resume();
-                console.log('播放倍数', 1 / timeWarp)
-            })
-        }
 
-        handleLoadFile("https://cloud-coach.ks3-cn-beijing.ksyuncs.com/1669293957359紫罗兰永恒花园ED1——《みちしるべ》.mid")
 
-        MIDI.Player.currentTime = 100000
+        // MIDI.Player.currentTime = 100000
         // MIDI.Player.timeWarp = 0.6289308176100629
-        setInterval(() => {
-            // console.log(MIDI.Player.currentTime / 1000, MIDI.Player.restart)
-        }, 100)
+        // setInterval(() => {
+        //     // console.log(MIDI.Player.currentTime / 1000, MIDI.Player.restart)
+        // }, 100)
 
         var playBtn = document.getElementById('playBtn')
-        var pauseBtn = document.getElementById('pauseBtn')
-        var stopBtn = document.getElementById('stopBtn')
-        var skipBtn = document.getElementById('skipBtn')
+        // var pauseBtn = document.getElementById('pauseBtn')
+        // var stopBtn = document.getElementById('stopBtn')
+        // var skipBtn = document.getElementById('skipBtn')
         playBtn.addEventListener('click', () => {
+            MIDI.noteOn(0, 50, 100, 0);
+            MIDI.noteOff(0, 50, 4);
             console.log(MIDI)
-            MIDI.Player.resume()
         })
-        pauseBtn.onclick = function () {
-            MIDI.Player.pause();
-        }
-        stopBtn.onclick = function () {
-            MIDI.Player.currentTime = 100000
-            MIDI.Player.resume();
-            console.log(MIDI)
-        }
-        skipBtn.onchange = function (e) {
-            var timeWarp = Number(e.target.value) / 100
-            // MIDI.Player.stop()
-            // const oldCurrent = MIDI.Player.currentTime
-            // const ab = MIDI.Player.replayer.getData()
-            // console.log(ab)
-            MIDI.Player.loadMidiFile(() => {
-                MIDI.Player.timeWarp = 1 / timeWarp
-                console.log('播放倍数', 1 / timeWarp, Number(e.target.value))
-                MIDI.Player.resume();
-            })
+        // pauseBtn.onclick = function () {
+        //     MIDI.Player.pause();
+        // }
+        // stopBtn.onclick = function () {
+        //     MIDI.Player.currentTime = 100000
+        //     MIDI.Player.resume();
+        //     console.log(MIDI)
+        // }
+        // skipBtn.onchange = function (e) {
+        //     var timeWarp = Number(e.target.value) / 100
+        //     // MIDI.Player.stop()
+        //     // const oldCurrent = MIDI.Player.currentTime
+        //     // const ab = MIDI.Player.replayer.getData()
+        //     // console.log(ab)
+        //     MIDI.Player.loadMidiFile(() => {
+        //         MIDI.Player.timeWarp = 1 / timeWarp
+        //         console.log('播放倍数', 1 / timeWarp, Number(e.target.value))
+        //         MIDI.Player.resume();
+        //     })
 
 
-        }
-    </script>
-    <script>
-        console.log(MIDI)
+        // }
     </script>
 </body>
 

+ 5 - 4
src/state.ts

@@ -194,9 +194,10 @@ export const onTimeupdate = (evt: Event) => {};
 
 /** 播放模式结束自动重播 */
 const autoResetPlay = () => {
-	// 没有开启自动重播, 不是练习模式
-	if (!state.setting.repeatAutoPlay || state.modeType !== "practise") return;
+	if (state.modeType !== "practise") return;
 	skipNotePlay(0, true);
+	// 没有开启自动重播, 不是练习模式
+	if (!state.setting.repeatAutoPlay) return;
 	scrollViewNote();
 	setTimeout(() => {
 		togglePlay("play");
@@ -240,7 +241,7 @@ const handlePlaying = () => {
 					return;
 				}
 				item = selectStartItem;
-				setAudioCurrentTime(selectStartItem.time);
+				setAudioCurrentTime(selectStartItem.time, selectStartItem.i);
 			}
 		}
 		gotoNext(item);
@@ -257,7 +258,7 @@ export const skipNotePlay = (itemIndex: number, isStart = false) => {
 	}
 	// console.log("🚀 ~ itemTime:", itemTime);
 	if (item) {
-		setAudioCurrentTime(itemTime);
+		setAudioCurrentTime(itemTime, itemIndex);
 		gotoNext(item);
 	}
 };

+ 84 - 35
src/view/audio-list/index.tsx

@@ -1,13 +1,21 @@
-import { computed, defineComponent, reactive } from "vue";
+import { computed, defineComponent, reactive, ref } from "vue";
 import state, { onEnded, onLoadedmetadata, onPlay, onTimeupdate } from "../../state";
 import styles from "./index.module.less";
+import { getMidiCurrentTime, getMidiDuration, handleTogglePlayMidi, hanldeInitMidiData, hanldeSetMidiPlaybackRate, setMidiCurrentTime } from "./midiPlayer";
 
 const audioData = reactive({
 	songEle: null as unknown as HTMLAudioElement,
 	backgroundEle: null as unknown as HTMLAudioElement,
+	midiRender: false,
 });
+const midiRef = ref();
 /** 播放或暂停 */
 export const audioListStart = (type: "play" | "paused") => {
+	// 如果是midi播放
+	if (audioData.midiRender) {
+		handleTogglePlayMidi(type);
+		return;
+	}
 	if (type === "play") {
 		audioData.songEle?.play();
 		audioData.backgroundEle?.play();
@@ -18,21 +26,41 @@ export const audioListStart = (type: "play" | "paused") => {
 };
 /** 设置倍数播放 */
 export const setAudioPlaybackRate = (rate: number) => {
+	// 如果是midi播放
+	if (audioData.midiRender) {
+		hanldeSetMidiPlaybackRate(rate);
+		return;
+	}
 	audioData.songEle && (audioData.songEle.playbackRate = rate);
 	audioData.backgroundEle && (audioData.backgroundEle.playbackRate = rate);
 };
 
 /** 获取当前播放的时间 */
 export const getAudioCurrentTime = () => {
+	// 如果是midi播放
+	if (audioData.midiRender) {
+		const c = getMidiCurrentTime()
+		return c
+	}
 	return audioData.songEle?.currentTime || audioData.backgroundEle?.currentTime || 0;
 };
 /** 获取曲谱的总时间 */
 export const getAudioDuration = () => {
+	// 如果是midi播放
+	if (audioData.midiRender) {
+		const d = getMidiDuration()
+		return d
+	}
 	return audioData.songEle?.duration || audioData.backgroundEle?.duration || 0;
 };
 
 /** 设置播放的开始时间 */
-export const setAudioCurrentTime = (time: number) => {
+export const setAudioCurrentTime = (time: number, index = 0) => {
+	// 如果是midi播放
+	if (audioData.midiRender) {
+		setMidiCurrentTime(index)
+		return;
+	}
 	audioData.songEle && (audioData.songEle.currentTime = time);
 	audioData.backgroundEle && (audioData.backgroundEle.currentTime = time);
 };
@@ -45,41 +73,62 @@ export default defineComponent({
 			return state.playSource === "music";
 		});
 
-		// const music = new Howl({
-		// 	src: tockAndTick.tick,
-		// })
-		// const accompany = new Howl({
-		// 	src: tockAndTick.tock,
-		// })
-		// console.log(state.music, music);
+		/** iframe 加载完成后, 加载midiURL */
+		const handleLoad = () => {
+			midiRef.value.contentWindow.handleRendered = () => {
+				audioData.midiRender = true;
+			};
+			hanldeInitMidiData(midiRef.value);
+
+			// const result = {
+			// 	src: state.midiUrl,
+			// 	bpm: state.speed,
+			// };
+
+			// // midiRef.value.contentWindow.handleLoadFile(result, () => {
+			// 	// console.log("文件加载成功", state.speed);
+			// 	// midiRef.value.contentWindow.setMusicSpeed(state.speed || 100)
+			// // });
+			// midiRef.value.contentWindow.onPlay = () => {
+			// 	onPlay();
+			// };
+			// midiRef.value.contentWindow.onEnded = () => {
+			// 	onEnded();
+			// };
+		};
+
+		console.log(state.playMode, state.midiUrl);
 		return () => (
 			<div class={styles.audioList}>
-				<div>
-					<audio
-						muted={!isMusicMuted.value}
-						preload="auto"
-						ref={(el) => {
-							if (state.music) {
-								audioData.songEle = el as HTMLAudioElement;
-							}
-						}}
-						src={state.music}
-						onLoadedmetadata={onLoadedmetadata}
-						onPlay={onPlay}
-						onTimeupdate={onTimeupdate}
-						onEnded={onEnded}
-					/>
-					<audio
-						muted={isMusicMuted.value}
-						preload="auto"
-						ref={(el) => {
-							if (state.accompany) {
-								audioData.backgroundEle = el as HTMLAudioElement;
-							}
-						}}
-						src={state.accompany}
-					/>
-				</div>
+				{state.playMode === "MIDI" && state.speed != 0 && <iframe style={{ display: "none" }} ref={midiRef} src={`/midi/index.html`} onLoad={handleLoad} />}
+				{state.playMode !== "MIDI" && (
+					<div>
+						<audio
+							muted={!isMusicMuted.value}
+							preload="auto"
+							ref={(el) => {
+								if (state.music) {
+									audioData.songEle = el as HTMLAudioElement;
+								}
+							}}
+							src={state.music}
+							onLoadedmetadata={onLoadedmetadata}
+							onPlay={onPlay}
+							onTimeupdate={onTimeupdate}
+							onEnded={onEnded}
+						/>
+						<audio
+							muted={isMusicMuted.value}
+							preload="auto"
+							ref={(el) => {
+								if (state.accompany) {
+									audioData.backgroundEle = el as HTMLAudioElement;
+								}
+							}}
+							src={state.accompany}
+						/>
+					</div>
+				)}
 			</div>
 		);
 	},

+ 73 - 0
src/view/audio-list/midiPlayer.tsx

@@ -0,0 +1,73 @@
+import { defineComponent, reactive } from "vue";
+import state, { gotoNext, onEnded, onPlay } from "/src/state";
+
+let timer: any = null;
+const midiData = reactive({
+    iframeRef: null as any,
+    playing: false,
+    index: 0,
+    /** 倍数播放 */
+    rate: 1
+})
+
+const playNote = () => {
+    if (!midiData.playing) return
+    const item = state.times[midiData.index]
+    // 播放到最有一个音符,结束
+    if (!item){
+        onEnded()
+        return
+    }
+    midiData.index++
+    const duration = item.endtime - item.time
+    midiData.iframeRef?.contentWindow?.playNote(item.realKey, duration)
+    // gotoNext(item)
+    timer = setTimeout(() => {
+        playNote()
+    }, duration / midiData.rate * 1000)
+}
+/** 停止播放 */
+const stopPlay = () => {
+    midiData.playing = false
+}
+/** 初始化 */
+export const hanldeInitMidiData = (iframeRef: HTMLIFrameElement) => {
+    midiData.iframeRef = iframeRef
+}
+/** 切换播放 */
+export const handleTogglePlayMidi = (type: "play" | "paused") => {
+    if (type === 'play'){
+        midiData.playing = true
+        playNote()
+        onPlay()
+    } else {
+        stopPlay()
+    }
+}
+/** 设置倍数播放 */
+export const hanldeSetMidiPlaybackRate = (rate: number) => {
+    midiData.rate = rate
+}
+/** 获取播放时间 */
+export const getMidiCurrentTime = () => {
+    let index = midiData.index - 1
+    index = index < 0 ? 0 : index
+    return state.times[index].time
+}
+/** 获取总播放时间 */
+export const getMidiDuration = () => {
+    return state.times[state.times.length - 1].endtime
+}
+/** 设置播放时间 */
+export const setMidiCurrentTime = (index: number) => {
+    clearTimeout(timer)
+    midiData.index = index
+    playNote()
+}
+
+export default defineComponent({
+    name: 'midiPlayer',
+    setup(){
+        return () => <div style={{display: 'none'}}></div>
+    }
+})