feat: sustain (CC64), modulation (CC1), pitch bend for MIDI keyboard
- Add controllerChange forwarder for all CC messages (cmd 0xB) - Add pitchBend method to SonicSF + MIDI handler (cmd 0xE) - CCs routed to _synthInstance.controllerChange(ch, cc, val) - Pitch bend routed to _synthInstance.pitchBend(ch, 14bit-val)
This commit is contained in:
+40
-19
@@ -6753,25 +6753,26 @@ const App = () => {
|
||||
const pitch = msg.data[1];
|
||||
const velocity = msg.data[2];
|
||||
if (cmd === 0x9 && velocity > 0) {
|
||||
lastMidiNoteRef.current = { pitch, velocity, startTime: performance.now(), length: 0 };
|
||||
setLastMidiNote({ pitch, velocity, length: 0, time: Date.now() });
|
||||
activeMidiPitchesRef.current.add(pitch);
|
||||
setActiveMidiPitches(new Set(activeMidiPitchesRef.current));
|
||||
{ if (window.SonicSF) {
|
||||
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, 500, undefined, arSub.instrumentProgram, null, undefined, 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, 500, undefined, prog, dest, undefined, se);
|
||||
}
|
||||
}
|
||||
} }
|
||||
} else if (cmd === 0x8 || (cmd === 0x9 && velocity === 0)) {
|
||||
lastMidiNoteRef.current = { pitch, velocity, startTime: performance.now(), length: 0 };
|
||||
setLastMidiNote({ pitch, velocity, length: 0, time: Date.now() });
|
||||
activeMidiPitchesRef.current.add(pitch);
|
||||
setActiveMidiPitches(new Set(activeMidiPitchesRef.current));
|
||||
{ if (window.SonicSF) {
|
||||
const ch = msg.data[0] & 0x0F;
|
||||
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, 500, undefined, arSub.instrumentProgram, null, undefined, 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, 500, undefined, prog, dest, undefined, se);
|
||||
}
|
||||
}
|
||||
} }
|
||||
} else if (cmd === 0x8 || (cmd === 0x9 && velocity === 0)) {
|
||||
activeMidiPitchesRef.current.delete(pitch);
|
||||
setActiveMidiPitches(new Set(activeMidiPitchesRef.current));
|
||||
const current = lastMidiNoteRef.current;
|
||||
@@ -6780,6 +6781,26 @@ const App = () => {
|
||||
lastMidiNoteRef.current = { ...current, length: lenSec };
|
||||
setLastMidiNote(prev => prev && prev.pitch === pitch ? { ...prev, length: lenSec, time: Date.now() } : prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Sustain (CC64), Modulation (CC1), Pitch Bend ──
|
||||
const midiCh = msg.data[0] & 0x0F;
|
||||
if (cmd === 0xB) {
|
||||
// Controller Change: forward all CCs to SpessaSynth
|
||||
const cc = msg.data[1];
|
||||
const val = msg.data[2];
|
||||
if (window.SonicSF && window.SonicSF.controllerChange) {
|
||||
window.SonicSF.controllerChange(midiCh, cc, val);
|
||||
}
|
||||
} else if (cmd === 0xE) {
|
||||
// Pitch Bend: 14-bit value (LSB + MSB)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Forward to active MIDI recorders
|
||||
|
||||
Reference in New Issue
Block a user