From: <sv...@op...> - 2024-12-21 19:52:44
|
Author: sagamusix Date: Sat Dec 21 20:52:32 2024 New Revision: 22595 URL: https://source.openmpt.org/browse/openmpt/?op=revision&rev=22595 Log: [Fix] Avoid NNA channel starvation because of sample-based instruments that have a MIDI channel assigned. Channel starvation can still happen if a VST plugin or the MIDI I/O plugin is associated with the instrument. Other built-in plugins won't have this issue (https://bugs.openmpt.org/view.php?id=1848). Modified: trunk/OpenMPT/soundlib/Snd_fx.cpp Modified: trunk/OpenMPT/soundlib/Snd_fx.cpp ============================================================================== --- trunk/OpenMPT/soundlib/Snd_fx.cpp Sat Dec 21 20:26:11 2024 (r22594) +++ trunk/OpenMPT/soundlib/Snd_fx.cpp Sat Dec 21 20:52:32 2024 (r22595) @@ -2233,16 +2233,19 @@ for(CHANNELINDEX i = GetNumChannels(); i < m_PlayState.Chn.size(); i++) { const ModChannel &c = m_PlayState.Chn[i]; - // No sample and no plugin playing - if(!c.nLength && !c.HasMIDIOutput()) + // Sample playing? + if(c.nLength) + continue; + // Can a plugin potentially be playing? + if(!c.HasMIDIOutput()) return i; - // Plugin channel with already released note - if(!c.nLength && c.dwFlags[CHN_KEYOFF | CHN_NOTEFADE]) + // Has the plugin note already been released? (note: lastMidiNoteWithoutArp is set from within IMixPlugin, so this implies that there is a valid plugin assignment) + if(c.dwFlags[CHN_KEYOFF | CHN_NOTEFADE] || c.lastMidiNoteWithoutArp == NOTE_NONE) return i; } - uint32 vol = 0x800000; - if(nChn < MAX_CHANNELS) + int32 vol = 0x800100; + if(nChn < m_PlayState.Chn.size()) { const ModChannel &srcChn = m_PlayState.Chn[nChn]; if(!srcChn.nFadeOutVol && srcChn.nLength) @@ -2264,7 +2267,8 @@ // Use a combination of real volume [14 bit] (which includes volume envelopes, but also potentially global volume) and note volume [9 bit]. // Rationale: We need volume envelopes in case e.g. all NNA channels are playing at full volume but are looping on a 0-volume envelope node. // But if global volume is not applied to master and the global volume temporarily drops to 0, we would kill arbitrary channels. Hence, add the note volume as well. - uint32 v = (c.nRealVolume << 9) | c.nVolume; + int32 v = (c.nRealVolume << 9) | c.nVolume; + // Less priority to looped samples if(c.dwFlags[CHN_LOOP]) v /= 2; if((v < vol) || ((v == vol) && (c.VolEnv.nEnvPosition > envpos))) |