Pitch Shifter - artifacts

Hi I m implementing pitch shifter. I have done some tests on sinus wave and when i set my ratio = 2 so i want to change pitch one octave up i got some artifacts:
obraz

So i as you see on the picture above i was able to chagne pitch correctly but i got some unwanted freqs in signal.
When I set a ratio = 1 :


the pitch was unchanged, as wanted and also i didnt get any artifacts.

I assume that the problem is with FFT size and hop size, because when i was changing those values the results was different some got more artifacts, some less.

Is there a rule how to set a FFT size and hop size to get the best results without artifacts? Or maybe there can be a mistake in my implementation?

With this vaules it worked the best:
my buffer size =441;
fftSize = 64;
hopSize = fftSize/8;

Thanks is advance!

Also i noticed that when i change the ratio the sum of artifacts is changing…
This is my code in procces block:

  my_fft.perform(preFFTBuffer.data(), fftBuffer.data(), false);
            
            for (int i = 0; i < fftSize / 2+1; i++)
            {

                // magnitude and phase calculation 
                double magnitude = getMagnitude(fftBuffer[i]);
                double phase = getPhase(fftBuffer[i]);  // in radians 

                /// calcualte phase diff between the last phase from previous hop and new one
                float phaseDiff = phase - previousInputPhases[i];

                // get Central Freq of bin
                float binCentreFrequency = getFreq(i);

                /// amount of phase increment we'd expect
                phaseDiff = wrapPhase(phaseDiff - binCentreFrequency * hopSize);  

                //deviation in (fractional) number of bins from the centre frequency
                float binDevation = phaseDiff * (float)fftSize / (float)hopSize / (2.0 * M_PI);

                // add the original bin number to get the fractional bin where this partial belongs 
                analysisFreqs[i] = (float)i + binDevation; 

                /// save magnitude for later
                analysisMagnitude[i] = magnitude;
                // saving phase for later 
                previousInputPhases[i] = phase;

            }

            // Zero out the synthesis bins, ready for new data
           //// std::fill(synthFreqs.begin(),synthFreqs.end(),0.f);
          ///  std::fill(synthMagnitude.begin(), synthMagnitude.end(), 0.f);
            for (int i = 0; i <= fftSize / 2; i++)
            {
                synthMagnitude[i] = synthFreqs[i] = 0;
            }

            for (int i = 0; i <= fftSize / 2; i++)
            {
                // finding the nearest bin to the shifted frequency 
                int newBin = floorf(i * ratio + 0.5);

                if (newBin <= fftSize / 2)
                {
                    synthMagnitude[newBin] += analysisMagnitude[i];
                    synthFreqs[newBin] = analysisFreqs[i] * ratio;
                }
            }

            for (int i = 0; i <= fftSize / 2; i++)
            {
                double amplitude = synthMagnitude[i];

                float binDeviation = synthFreqs[i] - i;

                float phaseDiff = binDeviation * 2.0 * M_PI * (float)hopSize / (float)fftSize;

                float binCentrFreq = 2.0 * M_PI * (float)i / (float)fftSize;

                phaseDiff += binCentrFreq * hopSize;

                float outPhase = wrapPhase(previousOutputPhases[i] + phaseDiff);

                /// converting phase and magnitude to complex numbers

                fftBuffer[i] = amplitude * cosf(outPhase) + (amplitude * sinf(outPhase) * 1i);

                if (i > 0 && i < fftSize / 2)
                {
                    fftBuffer[fftSize - i] = amplitude * cosf(outPhase) + (-(amplitude * sinf(outPhase) * 1i));
                }
                previousOutputPhases[i] = outPhase;
            }
            
            my_fft.perform(fftBuffer.data(), outputFFTBuffer.data(), true);