"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(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 (

MIDI Player: Hello World!

{isReady ? '播放器已就緒!' : '正在載入樂器...'}

); }