ace-step-webgpu / _source /src /hooks /useModel.js
shreyask's picture
Initial deploy: built app at root + source under _source/
24b9788 verified
import { useState, useRef, useCallback, useEffect } from "react";
export function useModel() {
const workerRef = useRef(null);
const audioUrlRef = useRef(null);
const [status, setStatus] = useState("idle");
const [message, setMessage] = useState("");
const [progress, setProgress] = useState(null);
const [audioUrl, setAudioUrl] = useState(null);
const [audioInfo, setAudioInfo] = useState(null);
const [error, setError] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
// Revoke a URL owned by this hook and forget it.
const revokeCurrentAudioUrl = useCallback(() => {
if (audioUrlRef.current) {
URL.revokeObjectURL(audioUrlRef.current);
audioUrlRef.current = null;
}
}, []);
useEffect(() => {
const worker = new Worker(new URL("../worker.js", import.meta.url), {
type: "module",
});
worker.onmessage = (e) => {
const { type, ...data } = e.data;
switch (type) {
case "status":
setMessage(data.message);
break;
case "progress":
setProgress(data);
break;
case "loaded":
setIsLoaded(true);
setStatus("ready");
setProgress(null);
break;
case "audio": {
// Revoke any previous URL owned by this hook before overwriting.
if (audioUrlRef.current) URL.revokeObjectURL(audioUrlRef.current);
const blob = new Blob([data.wavBuffer], { type: "audio/wav" });
const url = URL.createObjectURL(blob);
audioUrlRef.current = url;
setAudioUrl(url);
setAudioInfo({
duration: data.duration,
diffusionTime: data.diffusionTime,
totalTime: data.totalTime,
filename: `ace-step-${data.filenameStamp || Date.now()}.wav`,
});
setStatus("ready");
setMessage("Generation complete!");
break;
}
case "error":
setError(data.message);
setStatus("error");
console.error("Worker error:", data.message, data.stack);
break;
}
};
workerRef.current = worker;
return () => {
worker.terminate();
if (audioUrlRef.current) {
URL.revokeObjectURL(audioUrlRef.current);
audioUrlRef.current = null;
}
};
}, []);
const loadModel = useCallback(() => {
setStatus("loading");
setError(null);
workerRef.current?.postMessage({ type: "load" });
}, []);
const generate = useCallback(({ caption, lyrics, duration, shift, numSteps }) => {
setStatus("generating");
setError(null);
// Revoke the previous URL when user starts a new gen so the next "audio" message
// doesn't compete with a still-displayed blob.
revokeCurrentAudioUrl();
setAudioUrl(null);
setAudioInfo(null);
workerRef.current?.postMessage({
type: "generate",
caption,
lyrics,
duration,
shift,
numSteps,
});
}, [revokeCurrentAudioUrl]);
return {
status,
message,
progress,
audioUrl,
audioInfo,
error,
isLoaded,
loadModel,
generate,
};
}