fix: piano roll record via ARM + INPUT routes to piano roll tab
This commit is contained in:
+54
-4
@@ -4548,7 +4548,6 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
const [snapVal, setSnapVal] = React.useState('1/16');
|
const [snapVal, setSnapVal] = React.useState('1/16');
|
||||||
const [ccMode, setCcMode] = React.useState('velocity');
|
const [ccMode, setCcMode] = React.useState('velocity');
|
||||||
const [rollZoom, setRollZoom] = React.useState(60); // local horizontal zoom factor
|
const [rollZoom, setRollZoom] = React.useState(60); // local horizontal zoom factor
|
||||||
const [isArmed, setIsArmed] = React.useState(false);
|
|
||||||
const [aiBarStart, setAiBarStart] = React.useState(0);
|
const [aiBarStart, setAiBarStart] = React.useState(0);
|
||||||
const [aiBarEnd, setAiBarEnd] = React.useState(4);
|
const [aiBarEnd, setAiBarEnd] = React.useState(4);
|
||||||
|
|
||||||
@@ -5598,8 +5597,8 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
key: v,
|
key: v,
|
||||||
value: v
|
value: v
|
||||||
}, v)))), /*#__PURE__*/React.createElement("button", {
|
}, v)))), /*#__PURE__*/React.createElement("button", {
|
||||||
onClick: () => setIsArmed(!isArmed),
|
onClick: () => setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, isArmed: !s.isArmed } : s)),
|
||||||
className: `px-2 py-1 rounded text-xs font-bold ${isArmed ? 'bg-red-600 text-white' : 'bg-zinc-800 text-zinc-400'}`
|
className: `px-2 py-1 rounded text-xs font-bold ${st.isArmed ? 'bg-red-600 text-white' : 'bg-zinc-800 text-zinc-400'}`
|
||||||
}, "ARM"), /*#__PURE__*/React.createElement("select", {
|
}, "ARM"), /*#__PURE__*/React.createElement("select", {
|
||||||
value: selectedMidiInputId || '',
|
value: selectedMidiInputId || '',
|
||||||
onChange: e => onMidiInputSelect(e.target.value),
|
onChange: e => onMidiInputSelect(e.target.value),
|
||||||
@@ -6313,6 +6312,7 @@ const App = () => {
|
|||||||
|
|
||||||
const activeMIDIRecordersRef = useRef({});
|
const activeMIDIRecordersRef = useRef({});
|
||||||
const activeAudioRecordersRef = useRef({});
|
const activeAudioRecordersRef = useRef({});
|
||||||
|
const pianoRollRecorderRef = useRef(null);
|
||||||
const recordingPCMDataRef = useRef({});
|
const recordingPCMDataRef = useRef({});
|
||||||
const recordingStartTimeRef = useRef(0);
|
const recordingStartTimeRef = useRef(0);
|
||||||
const recordingSyncRef = useRef(null);
|
const recordingSyncRef = useRef(null);
|
||||||
@@ -9775,6 +9775,24 @@ const App = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if piano roll tab is active and armed
|
||||||
|
const activePianoRoll = subTabs.find(s => s.id === activeTab && s.type === 'PIANO_ROLL' && s.isArmed);
|
||||||
|
if (activePianoRoll && selectedMidiInputId) {
|
||||||
|
setRecordingState('COUNT_IN');
|
||||||
|
const secondsPerBeat = 60.0 / (parseInt(bpm) || 120);
|
||||||
|
const countInDuration = secondsPerBeat * 4;
|
||||||
|
const audioCtx = getAudioContext();
|
||||||
|
const now = audioCtx.currentTime;
|
||||||
|
showToast('Metronome Count-in: 4... 3... 2... 1... (MIDI Piano Roll)', 'info');
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
playMetronomeClick(now + i * secondsPerBeat, i === 0);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
startPianoRollRecording(activePianoRoll);
|
||||||
|
}, countInDuration * 1000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const armed = activeTracks.filter(t => t.isArmed && t.inputSource?.deviceType && t.inputSource.deviceType !== 'NONE');
|
const armed = activeTracks.filter(t => t.isArmed && t.inputSource?.deviceType && t.inputSource.deviceType !== 'NONE');
|
||||||
if (armed.length === 0) {
|
if (armed.length === 0) {
|
||||||
showToast('Vui lòng Arm (R) ít nhất một track và chọn cổng Input để ghi âm.', 'warning');
|
showToast('Vui lòng Arm (R) ít nhất một track và chọn cổng Input để ghi âm.', 'warning');
|
||||||
@@ -9798,6 +9816,22 @@ const App = () => {
|
|||||||
}, countInDuration * 1000);
|
}, countInDuration * 1000);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const startPianoRollRecording = (tab) => {
|
||||||
|
const context = getAudioContext();
|
||||||
|
const secondsPerBeat = 60.0 / (parseInt(bpm) || 120);
|
||||||
|
const startTime = currentTime;
|
||||||
|
const midiRec = new ClientMIDIRecorder(context, parseInt(bpm) || 120, 4);
|
||||||
|
midiRec.tempTabId = tab.id;
|
||||||
|
pianoRollRecorderRef.current = midiRec;
|
||||||
|
activeMIDIRecordersRef.current['piano_roll'] = midiRec;
|
||||||
|
if (selectedMidiInputId) midiRec.setSelectedMidiInputId(selectedMidiInputId);
|
||||||
|
setRecordingState('RECORDING');
|
||||||
|
setRecTempMidiNotes([]);
|
||||||
|
startTrackPlayback(startTime);
|
||||||
|
setIsPlaying(true);
|
||||||
|
showToast('Recording MIDI to Piano Roll...', 'info');
|
||||||
|
};
|
||||||
|
|
||||||
const startRecordingTake = async (armedTracks) => {
|
const startRecordingTake = async (armedTracks) => {
|
||||||
const context = getAudioContext();
|
const context = getAudioContext();
|
||||||
if (context.state === 'suspended') {
|
if (context.state === 'suspended') {
|
||||||
@@ -9986,7 +10020,23 @@ const App = () => {
|
|||||||
const midiRec = midiRecorders[trackId];
|
const midiRec = midiRecorders[trackId];
|
||||||
const recordedNotes = midiRec.stop();
|
const recordedNotes = midiRec.stop();
|
||||||
|
|
||||||
if (midiRec.tempMidiItemId) {
|
if (midiRec.tempTabId) {
|
||||||
|
const recordedNotes = midiRec.stop();
|
||||||
|
if (recordedNotes.length > 0) {
|
||||||
|
const newNotes = recordedNotes.map((n, i) => ({
|
||||||
|
id: 'rec_' + Date.now() + '_' + i,
|
||||||
|
pitch: n.pitch,
|
||||||
|
start_beat: Math.max(0, n.start_beat || 0),
|
||||||
|
duration_beats: Math.max(0.125, n.duration_beats || 0.25),
|
||||||
|
velocity: Math.min(1, (n.velocity || 0.8)),
|
||||||
|
pan: 0.0
|
||||||
|
}));
|
||||||
|
setSubTabs(prev => prev.map(s => s.id === midiRec.tempTabId ? { ...s, notes: [...(s.notes || []), ...newNotes], isDirty: true } : s));
|
||||||
|
setCanvasRedrawCount(n => n + 1);
|
||||||
|
showToast(`Đã ghi ${recordedNotes.length} notes vào Piano Roll.`, 'success');
|
||||||
|
}
|
||||||
|
hasRecordedAnything = true;
|
||||||
|
} else if (midiRec.tempMidiItemId) {
|
||||||
const recCurrentTimeSec = Math.max(0, context.currentTime - midiRec.recStartAudioTime - midiRec.latencyCompSec);
|
const recCurrentTimeSec = Math.max(0, context.currentTime - midiRec.recStartAudioTime - midiRec.latencyCompSec);
|
||||||
const recElapsedBeats = recCurrentTimeSec / (60.0 / midiRec.bpm);
|
const recElapsedBeats = recCurrentTimeSec / (60.0 / midiRec.bpm);
|
||||||
const totalDurationBeats = Math.max(4.0, recordedNotes.length > 0 ? Math.max(recElapsedBeats, ...recordedNotes.map(n => n.start_beat + n.duration_beats)) : recElapsedBeats);
|
const totalDurationBeats = Math.max(4.0, recordedNotes.length > 0 ? Math.max(recElapsedBeats, ...recordedNotes.map(n => n.start_beat + n.duration_beats)) : recElapsedBeats);
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user