private void WriteBlock(float previousGain, float currentGain, float[] source, float[] destination)
        {
            if (Math.Max(previousGain, currentGain) < SoundFontMath.NonAudible)
            {
                return;
            }

            if (MathF.Abs(currentGain - previousGain) < 1.0E-3)
            {
                ArrayMath.MultiplyAdd(currentGain, source, destination);
            }
            else
            {
                var step = inverseBlockSize * (currentGain - previousGain);
                ArrayMath.MultiplyAdd(previousGain, step, source, destination);
            }
        }
        private void RenderBlock()
        {
            voices.Process();

            Array.Clear(blockLeft, 0, blockLeft.Length);
            Array.Clear(blockRight, 0, blockRight.Length);
            foreach (var voice in voices)
            {
                var previousGainLeft = masterVolume * voice.PreviousMixGainLeft;
                var currentGainLeft  = masterVolume * voice.CurrentMixGainLeft;
                WriteBlock(previousGainLeft, currentGainLeft, voice.Block, blockLeft);
                var previousGainRight = masterVolume * voice.PreviousMixGainRight;
                var currentGainRight  = masterVolume * voice.CurrentMixGainRight;
                WriteBlock(previousGainRight, currentGainRight, voice.Block, blockRight);
            }

            if (enableReverbAndChorus)
            {
                Array.Clear(chorusInputLeft, 0, chorusInputLeft.Length);
                Array.Clear(chorusInputRight, 0, chorusInputRight.Length);
                foreach (var voice in voices)
                {
                    var previousGainLeft = voice.PreviousChorusSend * voice.PreviousMixGainLeft;
                    var currentGainLeft  = voice.CurrentChorusSend * voice.CurrentMixGainLeft;
                    WriteBlock(previousGainLeft, currentGainLeft, voice.Block, chorusInputLeft);
                    var previousGainRight = voice.PreviousChorusSend * voice.PreviousMixGainRight;
                    var currentGainRight  = voice.CurrentChorusSend * voice.CurrentMixGainRight;
                    WriteBlock(previousGainRight, currentGainRight, voice.Block, chorusInputRight);
                }
                chorus.Process(chorusInputLeft, chorusInputRight, chorusOutputLeft, chorusOutputRight);
                ArrayMath.MultiplyAdd(masterVolume, chorusOutputLeft, blockLeft);
                ArrayMath.MultiplyAdd(masterVolume, chorusOutputRight, blockRight);

                Array.Clear(reverbInput, 0, reverbInput.Length);
                foreach (var voice in voices)
                {
                    var previousGain = reverb.InputGain * voice.PreviousReverbSend * (voice.PreviousMixGainLeft + voice.PreviousMixGainRight);
                    var currentGain  = reverb.InputGain * voice.CurrentReverbSend * (voice.CurrentMixGainLeft + voice.CurrentMixGainRight);
                    WriteBlock(previousGain, currentGain, voice.Block, reverbInput);
                }
                reverb.Process(reverbInput, reverbOutputLeft, reverbOutputRight);
                ArrayMath.MultiplyAdd(masterVolume, reverbOutputLeft, blockLeft);
                ArrayMath.MultiplyAdd(masterVolume, reverbOutputRight, blockRight);
            }
        }