Problem using pitch shifting with TD-PSOLA and formant preservation I tried out this implementation of TD-PSOLA (https://github.com/sannawag/TD-PSOLA) and even though I know how the algorithm works, I can't understand why I get NO audible differences in the output when using f_ratio = 0.5, given that f_ratio = f_new/f_orig.

I've tried printing the length of 'peaks' (that would be the total number of peaks) arrays from both, the original signal and the new signal, by using find_peaks() again after shift_pitch() returns the output. I got these results:

f_ratio                            #orig_peaks                    #new_peaks
1                                    417                            416
0.9                                  417                            389
0.8                                  417                            403
0.7                                  417                            396
0.6                                  417                            324
0.5                                  417                            404


This looks completely nonsense to me.

I've already tried using another windows (numpy's Hamming, Hanning). Of course, it has to have something to do with the overlap-and-add process...right?

Any help would be greatly appreciated.
I believe you're correct, it's an issue with the overlap-add process. The code doesn't correctly calculate the triangular windows for lower pitches.
In short, the windows are too big.
It produces incorrect results for any pitch ratio below 1, such as 0.5 (one octave down).
The diagram below explains the issue as I understand it:

The big windows introduce so many artifacts (additional peaks) that the result sounds almost exactly the same, apart from the &quot;speaking into fan&quot; effect.
To get around this, it would be easiest to add an if statement which would use the existing windows only when the pitch ratio is equal or greater than 1, otherwise it should use the 'ideal windows'.
However, I'm not sure to implement the 'ideal windows' in Python. The existing algorithm uses newPeaks to calculate windows. For lower pitches it must use the old peaks array. However, this requires new math which I'm not sure how to implement exactly.