midiplayer-pro2 / app /page.tsx
peter288's picture
Upload 10 files
c911c4d verified
"use client";
import { useEffect, useRef, useState } from 'react';
import MidiPlayer from 'midi-player-js';
import Soundfont from 'soundfont-player';
export default function HomePage() {
const audioCtxRef = useRef<AudioContext | null>(null);
const instrumentRef = useRef<any>(null);
const playerRef = useRef<any>(null);
const [soundfontLoading, setSoundfontLoading] = useState(true);
const [fileLoaded, setFileLoaded] = useState(false);
const [isPlaying, setIsPlaying] = useState(false);
const [tempo, setTempo] = useState(120);
const [fileFormat, setFileFormat] = useState<number | null>(null);
const [progress, setProgress] = useState(0);
useEffect(() => {
const AudioContext = window.AudioContext || (window as any).webkitAudioContext;
const audioCtx = new AudioContext();
audioCtxRef.current = audioCtx;
Soundfont.instrument(audioCtx, 'acoustic_grand_piano').then(instrument => {
instrumentRef.current = instrument;
const player = new MidiPlayer.Player((event: any) => {
if (!instrumentRef.current) return;
if (event.name === 'Note on' && event.velocity > 0) {
instrumentRef.current.play(event.noteName, audioCtx.currentTime, { gain: 1 });
} else if ((event.name === 'Note on' && event.velocity === 0) || event.name === 'Note off') {
instrumentRef.current.stop(event.noteName, audioCtx.currentTime);
}
});
player.on('endOfFile', () => {
setIsPlaying(false);
setProgress(0);
});
player.on('fileLoaded', () => {
try {
setFileFormat(player.getFormat());
} catch {
setFileFormat(null);
}
setTempo(120);
setFileLoaded(true);
setProgress(0);
});
playerRef.current = player;
setSoundfontLoading(false);
});
return () => {
audioCtx.close();
};
}, []);
useEffect(() => {
if (!playerRef.current) return;
let interval: any;
if (isPlaying) {
interval = setInterval(() => {
const remaining = playerRef.current.getSongPercentRemaining();
setProgress(100 - (remaining || 0));
}, 200);
}
return () => clearInterval(interval);
}, [isPlaying]);
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file && playerRef.current) {
const reader = new FileReader();
reader.onload = () => {
const arrayBuffer = reader.result;
if (!arrayBuffer) return;
playerRef.current.stop();
playerRef.current.loadArrayBuffer(arrayBuffer);
};
reader.readAsArrayBuffer(file);
}
};
const handlePlayPause = () => {
if (isPlaying) {
playerRef.current.pause();
} else {
playerRef.current.play();
}
setIsPlaying(!isPlaying);
};
const handleStop = () => {
playerRef.current.stop();
setIsPlaying(false);
setProgress(0);
};
const handleTempoChange = (newTempo: number) => {
playerRef.current.pause();
playerRef.current.setTempo(newTempo);
playerRef.current.play();
setTempo(newTempo);
setIsPlaying(true);
};
return (
<main className="max-w-xl mx-auto p-4">
<h1 className="text-3xl font-bold">♬ MidiPlayerJS</h1>
{soundfontLoading ? "Loading soundfont..." : (
<input type="file" accept=".mid,.midi" onChange={handleFileChange} />
)}
<button onClick={handlePlayPause} disabled={!fileLoaded}>
{isPlaying ? "Pause" : "Play"}
</button>
<button onClick={handleStop}>Stop</button>
<input type="range" min="50" max="200" value={tempo} onChange={(e) => handleTempoChange(Number(e.target.value))} />
<div>MIDI Format: {fileFormat}</div>
<div>Progress: {progress}%</div>
</main>
);
}