Using rubberband to pitch shift to a scale (Snapping the Values to the scale)

Hey all, how are you?

So, I’m doing some testing with Rubberband to see if it suits my needs, and the idea is to use it to create harmonies for vocals.

The issue is that I need a way to adjust the pitch live depending on the interval of the scale so that the pitched notes are actually part of the scale.

(Example: In C major doing a harmony in thirds would mean that from C to E there are 4 semitones, but if then the bottom note moves up to E, the third would have to be G, so 3 semitones up).

My idea for now is the following:

  1. Pitch shift the harmony 3 semitones up.
  2. Take the incoming, original pitch (not shifted) and use it to detect what note of the key it is.
  3. Then, depending on what interval that note is, return a 1 or a 0 and add that to the pitch shift, so that instead of shifting up 3 semitones, it can vary between 3 and 4.

However, for now I have not been too successful lol!
This is what I got so far (with some help from AI)

     int isFullStepOrHalfStep(float freq, int keyThatBeginsScale, int majorOrMinor)
        {
        // Get the closest frequency in the scale
        float closestFreq = getClosestKeyFreqInScale(freq, keyThatBeginsScale, majorOrMinor);
        int currentKeyNum = 0;

        // Find the key number for our frequency
        for (int i = 1; i <= NUM_PIANO_KEYS; i++) {
            if (std::fabs(keyFreq[i] - closestFreq) < 0.01) {
                currentKeyNum = i;
                break;
            }
        }

        // If we couldn't find the key number, return -1 for error
        if (currentKeyNum == 0) return -1;

        // Find position in scale (0-6, representing steps between notes)
        int scalePosition = 0;
        int tempKey = keyThatBeginsScale;
        const int* scaleSteps = (majorOrMinor == 1) ? majorStep : minorStep;

        while (tempKey < currentKeyNum) {
            tempKey += scaleSteps[scalePosition];
            scalePosition = (scalePosition + 1) % (NUM_OF_KEYS_IN_SCALE - 1);
        }

        // Now scalePosition points to the next step in the scale
        // If we're at position 2 or 6 in major scale (E->F or B->C), it's a half step
        // If we're at position 1 or 4 in minor scale (B->C or E->F), it's a half step
        if (majorOrMinor == 1) {  // Major scale
            return (scalePosition != 2 && scalePosition != 6);
        } else {  // Minor scale
            return (scalePosition != 1 && scalePosition != 4);
        }
    }

I sort of feel like this might not be the best approach, although shifting up a semitone or not depending on the interval seems pretty doable.

Any ideas? Or do you know of a more typical way this is usually done?

Thanks!