import React, { useRef, useState, useEffect } from 'react'; import { initWebMidi, changeMidiOutputPort, changeMidiInputPort, registerMidiLogger, requestCurrentPatchDump, requestAllDump, changeKeyboardInputPort, 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(props) { 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(); const [activeKeyboardId, setActiveKeyboardId] = useState(''); 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 { console.log('[MIDI] longueur SysEx:', rawSysExBytes.length); console.log( '[MIDI] RAW:', Array.from(rawSysExBytes) .map(b => b.toString(16).padStart(2, '0')) .join(' ') ); const parsedStoreData = return_store_from_file(rawSysExBytes); if (!parsedStoreData.patch.name.trim()) { parsedStoreData.patch.name = '------------'; } console.log('[MIDI] patch décodé:', parsedStoreData.patch); console.log('[MIDI] entités:', Object.keys(parsedStoreData)); // Injection explicite sous forme de tableau d'entités dispatch(parameterFromFile(Object.values(parsedStoreData))); console.log('[MIDI] Redux mis à jour'); } catch (error) { console.error('[MIDI] erreur décodage/injection:', 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 (