fix: multi-track ARM routes MIDI to all armed tracks
- Use filter(t => t.isArmed) instead of find() to route MIDI input to ALL armed tracks simultaneously - Use getTrackMidiChannel per track instead of raw MIDI hardware channel (msg.data[0] & 0x0F) - NoteOff, CC, PitchBend also routed to each armed track's dedicated channel - Previous code sent to channel 0 + first armed track only
This commit is contained in:
+55
-26
@@ -6954,7 +6954,7 @@ const App = () => {
|
||||
input.onmidimessage = msg => {
|
||||
console.log(`[DevLog] [Raw MIDI] Received from "${input.name}" (ID: ${input.id}) - Data:`, Array.from(msg.data));
|
||||
if (msg.data.length < 3) return;
|
||||
const cmd = msg.data[0] >> 4;
|
||||
const cmd = msg.data[0] >> 4;
|
||||
const pitch = msg.data[1];
|
||||
const velocity = msg.data[2];
|
||||
if (cmd === 0x9 && velocity > 0) {
|
||||
@@ -6962,25 +6962,30 @@ const App = () => {
|
||||
setLastMidiNote({ pitch, velocity, length: 0, time: Date.now() });
|
||||
activeMidiPitchesRef.current.add(pitch);
|
||||
setActiveMidiPitches(new Set(activeMidiPitchesRef.current));
|
||||
// Stop previous notes on this channel before playing new note
|
||||
// Only send All Notes Off when sustain pedal is not active
|
||||
if (window.SonicSF) {
|
||||
const ch = msg.data[0] & 0x0F;
|
||||
if (!window.SonicSF.sustainActive || !window.SonicSF.sustainActive(ch)) {
|
||||
try { window.SonicSF.controllerChange(ch, 123, 0); } catch (e) {}
|
||||
}
|
||||
const arSub = subTabsRef && subTabsRef.current && activeTabRef && subTabsRef.current.find(s => s.id === activeTabRef.current && s.type === 'PIANO_ROLL' && s.isArmed);
|
||||
if (arSub) {
|
||||
window.SonicSF.playNote(pitch, velocity, 60000, undefined, arSub.instrumentProgram, null, ch, arSub.synth_engine);
|
||||
} else {
|
||||
const armedTrack = activeTracksRef.current ? activeTracksRef.current.find(t => t.isArmed) : null;
|
||||
if (armedTrack) {
|
||||
const prog = armedTrack.instrumentProgram;
|
||||
const se = armedTrack.synth_engine;
|
||||
const dest = activeTrackNodesRef.current[armedTrack.id]?.gainNode || null;
|
||||
window.SonicSF.playNote(pitch, velocity, 60000, undefined, prog, dest, ch, se);
|
||||
}
|
||||
// Route MIDI input to ALL armed tracks on their dedicated channels
|
||||
// Do NOT use raw MIDI hardware channel (msg.data[0] & 0x0F)
|
||||
if (window.SonicSF) {
|
||||
var allTracks = activeTracksRef.current || [];
|
||||
var arSubs = subTabsRef && subTabsRef.current ? subTabsRef.current.filter(function(s) { return s.type === 'PIANO_ROLL' && s.isArmed; }) : [];
|
||||
var armedTracks = allTracks.filter(function(t) { return t.isArmed; });
|
||||
// Piano Roll arming has priority: route to each armed sub-tab's parent track
|
||||
if (arSubs.length > 0) {
|
||||
arSubs.forEach(function(as) {
|
||||
var asTrk = allTracks.find(function(t) { return t.id === as.trackId; });
|
||||
var asCh = window.SonicPianoRoll ? window.SonicPianoRoll.getTrackMidiChannel(asTrk, allTracks) : (asTrk ? asTrk.midiChannel : 0);
|
||||
var asProg = as.instrumentProgram;
|
||||
var asSe = as.synth_engine;
|
||||
window.SonicSF.playNote(pitch, velocity, 60000, undefined, asProg, null, asCh, asSe);
|
||||
});
|
||||
}
|
||||
// Route to ALL armed tracks (not just the first one)
|
||||
armedTracks.forEach(function(at) {
|
||||
var atCh = window.SonicPianoRoll ? window.SonicPianoRoll.getTrackMidiChannel(at, allTracks) : (at.midiChannel !== undefined ? at.midiChannel : 0);
|
||||
var atProg = at.instrumentProgram;
|
||||
var atSe = at.synth_engine;
|
||||
var atDest = activeTrackNodesRef.current[at.id]?.gainNode || null;
|
||||
window.SonicSF.playNote(pitch, velocity, 60000, undefined, atProg, atDest, atCh, atSe);
|
||||
});
|
||||
}
|
||||
} else if (cmd === 0x8 || (cmd === 0x9 && velocity === 0)) {
|
||||
activeMidiPitchesRef.current.delete(pitch);
|
||||
@@ -6991,29 +6996,53 @@ const App = () => {
|
||||
lastMidiNoteRef.current = { ...current, length: lenSec };
|
||||
setLastMidiNote(prev => prev && prev.pitch === pitch ? { ...prev, length: lenSec, time: Date.now() } : prev);
|
||||
}
|
||||
// Stop the note immediately via SpessaSynth
|
||||
// Stop the note on ALL armed tracks' dedicated channels
|
||||
if (window.SonicSF && window.SonicSF.stopNote) {
|
||||
const ch = msg.data[0] & 0x0F;
|
||||
window.SonicSF.stopNote(ch, pitch);
|
||||
var stopTracks = activeTracksRef.current || [];
|
||||
stopTracks.forEach(function(st) {
|
||||
if (!st.isArmed) return;
|
||||
var stCh = window.SonicPianoRoll ? window.SonicPianoRoll.getTrackMidiChannel(st, stopTracks) : (st.midiChannel !== undefined ? st.midiChannel : 0);
|
||||
window.SonicSF.stopNote(stCh, pitch);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ── Sustain (CC64), Modulation (CC1), Pitch Bend ──
|
||||
const midiCh = msg.data[0] & 0x0F;
|
||||
if (cmd === 0xB) {
|
||||
// Controller Change: forward all CCs to SpessaSynth
|
||||
// Controller Change: forward to ALL armed tracks' dedicated channels
|
||||
const cc = msg.data[1];
|
||||
const val = msg.data[2];
|
||||
if (window.SonicSF && window.SonicSF.controllerChange) {
|
||||
window.SonicSF.controllerChange(midiCh, cc, val);
|
||||
var ccTracks = activeTracksRef.current || [];
|
||||
var hasArmed = ccTracks.some(function(t) { return t.isArmed; });
|
||||
if (hasArmed) {
|
||||
ccTracks.forEach(function(ct) {
|
||||
if (!ct.isArmed) return;
|
||||
var ctCh = window.SonicPianoRoll ? window.SonicPianoRoll.getTrackMidiChannel(ct, ccTracks) : (ct.midiChannel !== undefined ? ct.midiChannel : 0);
|
||||
window.SonicSF.controllerChange(ctCh, cc, val);
|
||||
});
|
||||
} else {
|
||||
window.SonicSF.controllerChange(midiCh, cc, val);
|
||||
}
|
||||
}
|
||||
} else if (cmd === 0xE) {
|
||||
// Pitch Bend: 14-bit value (LSB + MSB)
|
||||
// Pitch Bend: forward to ALL armed tracks' dedicated channels
|
||||
const lsb = msg.data[1];
|
||||
const msb = msg.data[2];
|
||||
const bendVal = (msb << 7) | lsb;
|
||||
if (window.SonicSF && window.SonicSF.pitchBend) {
|
||||
window.SonicSF.pitchBend(midiCh, bendVal);
|
||||
var pbTracks = activeTracksRef.current || [];
|
||||
var hasArmedPB = pbTracks.some(function(t) { return t.isArmed; });
|
||||
if (hasArmedPB) {
|
||||
pbTracks.forEach(function(pt) {
|
||||
if (!pt.isArmed) return;
|
||||
var ptCh = window.SonicPianoRoll ? window.SonicPianoRoll.getTrackMidiChannel(pt, pbTracks) : (pt.midiChannel !== undefined ? pt.midiChannel : 0);
|
||||
window.SonicSF.pitchBend(ptCh, bendVal);
|
||||
});
|
||||
} else {
|
||||
window.SonicSF.pitchBend(midiCh, bendVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user