Spaces:
Sleeping
Sleeping
"use client"; | |
import { useEffect, useRef, useState } from 'react'; | |
import MidiPlayer from 'midi-player-js'; | |
import Soundfont from 'soundfont-player'; | |
// 這是一段 C 大調音階的 MIDI 檔案,使用 Base64 編碼寫死在程式碼中 | |
// 這樣就不需要使用者上傳檔案了 | |
const C_MAJOR_SCALE_BASE64 = 'data:audio/midi;base64,TVRoZAAAAAYAAQABAIxNVHJrAAAAGwD/BAcCBQNkgEN/AP8vAE1UcmsAAAAqAP8DBFNrYWxhAP8EDgIFA2SAQ38AkEIAAACQQwAAAJBFAACQSQAAAP8vAA=='; | |
export default function Home() { | |
const [isReady, setIsReady] = useState(false); | |
const player = useRef<MidiPlayer.Player | null>(null); | |
useEffect(() => { | |
// 整個初始化過程都在 useEffect 中,確保只在瀏覽器端執行 | |
const audioContext = new AudioContext(); | |
// 1. 載入樂器 (Soundfont) | |
Soundfont.instrument(audioContext, 'acoustic_grand_piano').then(instrument => { | |
// 2. 初始化 MIDI 播放器 | |
player.current = new MidiPlayer.Player(event => { | |
if (event.name === 'Note on' && event.velocity > 0) { | |
instrument.play(event.noteName); | |
} | |
}); | |
// 3. 載入我們寫死的 MIDI 資料 | |
player.current.loadDataUri(C_MAJOR_SCALE_BASE64); | |
// 4. 一切準備就緒,更新 UI | |
setIsReady(true); | |
}); | |
// 元件卸載時清理 AudioContext | |
return () => { | |
if (audioContext.state !== 'closed') { | |
audioContext.close(); | |
} | |
}; | |
}, []); // 空依賴陣列確保這個 effect 只執行一次 | |
const handlePlay = () => { | |
player.current?.play(); | |
}; | |
return ( | |
<main style={{ fontFamily: 'sans-serif', textAlign: 'center', paddingTop: '5rem' }}> | |
<h1>MIDI Player: Hello World!</h1> | |
<p>{isReady ? '播放器已就緒!' : '正在載入樂器...'}</p> | |
<button | |
onClick={handlePlay} | |
disabled={!isReady} | |
style={{ fontSize: '1.2rem', padding: '10px 20px', cursor: 'pointer' }} | |
> | |
🎵 播放 "Hello World" 音階 | |
</button> | |
</main> | |
); | |
} |