import React, { useRef, useState, useEffect } from 'react'; import { initWebMidi, changeMidiOutputPort, changeMidiInputPort, registerMidiLogger, requestCurrentPatchDump, registerPatchReceiver } from '../../utils/midiHelper'; import { Select } from '../utils/components'; import { useDispatch, useSelector } from 'react-redux'; import { parameterUpdateTab, parameterUpdated, parameterFromFile, parameterRefreshAll, selectEntities, } from '../editor/parameters/parameterSlice'; import { return_store_from_file, return_file_from_store } from '../io'; import download from 'downloadjs'; export default function Menu() { const [midiOutputs, setMidiOutputs] = useState([]); const [midiInputs, setMidiInputs] = useState([]); const [activeOutId, setActiveOutId] = useState(''); const [activeInId, setActiveInId] = useState(''); const [midiLogs, setMidiLogs] = useState([]); const consoleBottomRef = useRef(null); const [file, setFile] = useState(null); const fileRef = useRef(null); const state = useSelector((state) => selectEntities(state)); const activeTab = useSelector((state) => state.parameters.activeTab); const dispatch = useDispatch(); const reader = new FileReader(); useEffect(() => { // 1. Initialisation des ports MIDI physiques au démarrage initWebMidi().then(midiData => { setMidiOutputs(midiData.outputs); setMidiInputs(midiData.inputs); if (midiData.outputs.length > 0) setActiveOutId(midiData.outputs[0].id); if (midiData.inputs.length > 0) setActiveInId(midiData.inputs[0].id); }); // 2. Écoute active de la console d'activité noire registerMidiLogger((newLog) => { setMidiLogs((prevLogs) => { const updatedLogs = [...prevLogs, newLog]; return updatedLogs.slice(-30); // Limite l'historique à 30 lignes }); }); // 3. ÉCOUTE ET MISE À JOUR AUTOMATIQUE DU SON PROVENANT DU MICROKORG registerPatchReceiver((rawSysExBytes) => { try { // Convertit les octets physiques du microKORG en modèle de données exploitable par l'application const parsedStoreData = return_store_from_file(rawSysExBytes); // Injecte les données décodées dans le Store Redux pour forcer la mise à jour instantanée de l'écran dispatch(parameterFromFile(parsedStoreData)); console.log("[ALAPATCH] Structure des paramètres synchronisée avec le matériel."); } catch (error) { console.error("[ALAPATCH] Échec du décodage du dump matériel :", error); } }); }, [dispatch]); // Force le défilement automatique du moniteur vers le bas useEffect(() => { if (consoleBottomRef.current) { consoleBottomRef.current.scrollIntoView({ behavior: 'smooth' }); } }, [midiLogs]); const handleMidiOutChange = (e) => { const portId = e.target.value; setActiveOutId(portId); changeMidiOutputPort(portId); }; const handleMidiInChange = (e) => { const portId = e.target.value; setActiveInId(portId); changeMidiInputPort(portId); }; const loadFile = (f) => { reader.onloadend = (e) => { setFile(new Uint8Array(e.target.result)); }; reader.readAsArrayBuffer(f); }; useEffect(() => { if (file) { dispatch(parameterFromFile(return_store_from_file(file))); } }, [file, dispatch]); useEffect(() => { if (state.patch.mode === 'Vocoder') { dispatch(parameterUpdateTab('Vocoder')); } if (state.patch.mode === 'Single' || state.patch.mode === 'Multiple') { dispatch(parameterUpdateTab('Timbre1')); } }, [state.patch.mode, dispatch]); return (