import { useEffect, useMemo, useRef, useState } from "react" import { AudioStreamer } from "../AudioStreamer" import { VoiceChangerClient } from "../VoiceChangerClient" import { AudioStreamerSettingState, useAudioStreamerSetting } from "./useAudioStreamerSetting" import { ClientSettingState, useClientSetting } from "./useClientSetting" import { ServerSettingState, useServerSetting } from "./useServerSetting" import { useWorkletSetting, WorkletSettingState } from "./useWorkletSetting" export type UseClientProps = { audioContext: AudioContext | null audioOutputElementId: string } export type ClientState = { // 各種設定I/Fへの参照 workletSetting: WorkletSettingState clientSetting: ClientSettingState streamerSetting: AudioStreamerSettingState serverSetting: ServerSettingState // モニタリングデータ bufferingTime: number; responseTime: number; volume: number; outputRecordData: Float32Array[] | null; // Serverから帰ってきたデータをレコードしたもの // 情報取得 getInfo: () => Promise // 設定クリア clearSetting: () => Promise } export const useClient = (props: UseClientProps): ClientState => { // (1-1) クライアント const voiceChangerClientRef = useRef(null) const [voiceChangerClient, setVoiceChangerClient] = useState(voiceChangerClientRef.current) //// クライアント初期化待ち用フラグ const initializedResolveRef = useRef<(value: void | PromiseLike) => void>() const initializedPromise = useMemo(() => { return new Promise((resolve) => { initializedResolveRef.current = resolve }) }, []) // (1-2) 各種設定I/F const clientSetting = useClientSetting({ voiceChangerClient, audioContext: props.audioContext }) const streamerSetting = useAudioStreamerSetting({ voiceChangerClient }) const workletSetting = useWorkletSetting({ voiceChangerClient }) const serverSetting = useServerSetting({ voiceChangerClient }) // (1-3) モニタリングデータ const [bufferingTime, setBufferingTime] = useState(0) const [responseTime, setResponseTime] = useState(0) const [volume, setVolume] = useState(0) const [outputRecordData, setOutputRecordData] = useState(null) // (1-4) エラーステータス const errorCountRef = useRef(0) // (2-1) 初期化処理 useEffect(() => { const initialized = async () => { if (!props.audioContext) { return } const voiceChangerClient = new VoiceChangerClient(props.audioContext, true, { notifySendBufferingTime: (val: number) => { setBufferingTime(val) }, notifyResponseTime: (val: number) => { setResponseTime(val) }, notifyException: (mes: string) => { if (mes.length > 0) { console.log(`error:${mes}`) errorCountRef.current += 1 if (errorCountRef.current > 100) { alert("エラーが頻発しています。対象としているフレームワークのモデルがロードされているか確認してください。") errorCountRef.current = 0 } } } }, { notifyVolume: (vol: number) => { setVolume(vol) }, notifyOutputRecordData: (data: Float32Array[]) => { setOutputRecordData(data) } }) await voiceChangerClient.isInitialized() voiceChangerClientRef.current = voiceChangerClient setVoiceChangerClient(voiceChangerClientRef.current) console.log("[useClient] client initialized") const audio = document.getElementById(props.audioOutputElementId) as HTMLAudioElement audio.srcObject = voiceChangerClientRef.current.stream audio.play() initializedResolveRef.current!() } initialized() }, [props.audioContext]) // (2-2) 情報リロード const getInfo = useMemo(() => { return async () => { await initializedPromise await clientSetting.reloadClientSetting() // 実質的な処理の意味はない await serverSetting.reloadServerInfo() } }, [clientSetting, serverSetting]) const clearSetting = async () => { await clientSetting.clearSetting() await streamerSetting.clearSetting() await workletSetting.clearSetting() await serverSetting.clearSetting() } return { // 各種設定I/Fへの参照 clientSetting, streamerSetting, workletSetting, serverSetting, // モニタリングデータ bufferingTime, responseTime, volume, outputRecordData, // 情報取得 getInfo, // 設定クリア clearSetting, } }