Click after attack

in tutorial_synth_using_midi_input
I tried the exercise: Try adding a slower attack to the voices such that they don’t start abruptly. But after the attck of the sine there is always a click. I suppose that this is because of ramp values being not smooth enough. But changing the parameters seems not to help.

What I did:
In the struct SineWaveVoice I declared a private member
double fadeIn = 0.001.
In SineWaveVoice.startNote I set
tailOff = -1.0;
fadeIn = 0.001;

The member function renderNextBlock I expanded with an “else if” condition for attack: if tailOff < 0.0 then attack starts. And if fadeIn >= 0.999 tailOff is reset to 0.0;

void renderNextBlock (juce::AudioSampleBuffer& outputBuffer, int startSample, int numSamples) override
    {
        if (angleDelta != 0.0)
        {
            if (tailOff > 0.0) // [7]
            {
                while (--numSamples >= 0)
                {
                    auto currentSample = (float) (std::sin (currentAngle) * level * tailOff);

                    for (auto i = outputBuffer.getNumChannels(); --i >= 0;)
                        outputBuffer.addSample (i, startSample, currentSample);

                    currentAngle += angleDelta;
                    ++startSample;

                    tailOff *= 0.9999; // [8]

                    if (tailOff <= 0.005)
                    {
                        clearCurrentNote(); // [9]

                        angleDelta = 0.0;
                        break;
                    }
                }
            }
            else  if (tailOff < 0.0) //attack
            {
                while (--numSamples >= 0)
                {                    
                    auto currentSample = (float)(std::sin(currentAngle) * level * fadeIn);

                    for (auto i = outputBuffer.getNumChannels(); --i >= 0;)
                        outputBuffer.addSample(i, startSample, currentSample);

                    currentAngle += angleDelta;
                    ++startSample;
                    
                    fadeIn *= 1.0009;

                    if (fadeIn >= 0.999)
                    {               
                        tailOff = 0.0;
                        break;
                    }
                }
            }
            else
            {
                while (--numSamples >= 0) // [6]
                {
                    auto currentSample = (float) (std::sin (currentAngle) * level);

                    for (auto i = outputBuffer.getNumChannels(); --i >= 0;)
                        outputBuffer.addSample (i, startSample, currentSample);

                    currentAngle += angleDelta;
                    ++startSample;
                }
            }
        }
    }

Clicks disappear without extending the condition (no else if) but just introducing the member double realLevel = 0.001. Then in the renderNextBlock function I implement that member:

 void renderNextBlock (juce::AudioSampleBuffer& outputBuffer, int startSample, int numSamples) override
    {
        if (angleDelta != 0.0)
        {
          if (tailOff > 0.0) // [7]
          {
                        ...
          }        
          else
          {
              while (--numSamples >= 0) // [6]
              {
                if (realLevel < level) 
                {
                    realLevel *= 1.001;
                }
                auto currentSample = (float) (std::sin (currentAngle) * realLevel);
                ...
              }
            }
          }
       }