fix: ReferenceError assignTrackMidiChannel - chuyển allocator channel lên module-level cho PianoRollTabEditor + bật AudioWorklet thay ScriptProcessor
This commit is contained in:
+31
-27
@@ -12,6 +12,37 @@ const API_AUDIO = `${API_BASE_URL}/api/v1/audio`;
|
||||
const API_MULTITRACK = `${API_BASE_URL}/api/v1/multitrack`;
|
||||
const API_TASKS = `${API_BASE_URL}/api/v1/audio/tasks`;
|
||||
|
||||
// ── Dedicated per-track MIDI channel allocation ──
|
||||
// FluidSynth has 16 channels; if two tracks share a channel, arming one track
|
||||
// re-selects the other track's program and its instrument changes. Every track
|
||||
// gets its own stable, unique channel (0-15, skipping 9 which is the classic
|
||||
// percussion slot) so multi-track ARM / playback never cross-contaminates
|
||||
// instruments. Module-level so both App and the piano-roll sub-components
|
||||
// (PianoRollTabEditor etc.) allocate the SAME channel for a track.
|
||||
const trackMidiChannelsRef = { current: {} };
|
||||
const ensureTrackMidiChannel = (track, tracks) => {
|
||||
if (!track) return 0;
|
||||
const trackList = tracks || [];
|
||||
const inUse = new Set();
|
||||
trackList.forEach(tr => { if (tr && tr.id !== track.id && tr.midiChannel !== undefined) inUse.add(tr.midiChannel); });
|
||||
const cached = trackMidiChannelsRef.current[track.id];
|
||||
if (cached !== undefined && !inUse.has(cached)) return cached;
|
||||
const preferred = track.midiChannel !== undefined && !inUse.has(track.midiChannel) ? track.midiChannel : null;
|
||||
if (preferred !== null) { trackMidiChannelsRef.current[track.id] = preferred; return preferred; }
|
||||
for (let c = 0; c < 16; c++) {
|
||||
if (c === 9) continue;
|
||||
if (!inUse.has(c)) { trackMidiChannelsRef.current[track.id] = c; return c; }
|
||||
}
|
||||
trackMidiChannelsRef.current[track.id] = 0;
|
||||
return 0;
|
||||
};
|
||||
const assignTrackMidiChannel = (track, tracks) => {
|
||||
const ch = ensureTrackMidiChannel(track, tracks);
|
||||
if (track && track.midiChannel !== ch) track.midiChannel = ch;
|
||||
return ch;
|
||||
};
|
||||
|
||||
|
||||
// Handle ?sfs=<encoded> from double-clicking a .sfs file (opens domain -> loads project)
|
||||
(function handleSfsDeepLink() {
|
||||
try {
|
||||
@@ -11101,33 +11132,6 @@ const App = () => {
|
||||
"Tinkle Bell","Agogo","Steel Drums","Woodblock","Taiko Drum","Melodic Tom","Synth Drum","Reverse Cymbal",
|
||||
"Guitar Fret Noise","Breath Noise","Seashore","Bird Tweet","Telephone Ring","Helicopter","Applause","Gunshot"
|
||||
];
|
||||
// ── Dedicated per-track MIDI channel allocation ──
|
||||
// FluidSynth has 16 channels; if two tracks share a channel, arming one track
|
||||
// re-selects the other track's program and its instrument changes. Every track
|
||||
// gets its own stable, unique channel (0-15, skipping 9 which is the classic
|
||||
// percussion slot) so multi-track ARM never cross-contaminates instruments.
|
||||
const trackMidiChannelsRef = useRef({});
|
||||
const ensureTrackMidiChannel = (track, tracks) => {
|
||||
if (!track) return 0;
|
||||
const trackList = tracks || [];
|
||||
const inUse = new Set();
|
||||
trackList.forEach(tr => { if (tr && tr.id !== track.id && tr.midiChannel !== undefined) inUse.add(tr.midiChannel); });
|
||||
const cached = trackMidiChannelsRef.current[track.id];
|
||||
if (cached !== undefined && !inUse.has(cached)) return cached;
|
||||
const preferred = track.midiChannel !== undefined && !inUse.has(track.midiChannel) ? track.midiChannel : null;
|
||||
if (preferred !== null) { trackMidiChannelsRef.current[track.id] = preferred; return preferred; }
|
||||
for (let c = 0; c < 16; c++) {
|
||||
if (c === 9) continue;
|
||||
if (!inUse.has(c)) { trackMidiChannelsRef.current[track.id] = c; return c; }
|
||||
}
|
||||
trackMidiChannelsRef.current[track.id] = 0;
|
||||
return 0;
|
||||
};
|
||||
const assignTrackMidiChannel = (track, tracks) => {
|
||||
const ch = ensureTrackMidiChannel(track, tracks);
|
||||
if (track && track.midiChannel !== ch) track.midiChannel = ch;
|
||||
return ch;
|
||||
};
|
||||
const setTrackInstrumentWithProgram = (trackId, instrumentId, programNumber, displayName, bankNumber) => {
|
||||
const isSfInstrument = instrumentId && typeof instrumentId === 'string' && instrumentId.startsWith('sf_');
|
||||
const sfBank = bankNumber !== undefined ? bankNumber : (isSfInstrument ? 0 : undefined);
|
||||
|
||||
Reference in New Issue
Block a user