Your code is effectively using two effects:
-
Phase Vocoder:
This effect stretches the sound without changing the pitch.
-
Resample:
This affects both tempo and pitch, and is a bit counter-intuitive. When you resample to a higher sample rate, the number of samples increases, so when returned from Nyquist back into the audio track, it will occupy a greater length of the track (because the track’s sample rate is unaltered). Thus, increasing the sample rate causes the audio to play lower pitch and slower.
Conversely, reducing the sample rate causes the audio to play faster and higher.
Let’s take an easy example. Say that we want to slow the track to half speed, and double the frequency.
Consider the resampling first, as this is the only one that affects frequency.
We want “frequency x 2”, so we must resample to half the sample rate.
We know that this will also reduce the length to half the original.
Thus, to stretch the length (using Phase Vocoder, so that it does not change the pitch) to double the original, we must stretch by a factor of 4.
Converting that into code:
We want double the frequency and double the length, so we will call that a “stretch-factor” of 2.
(setf stretch 2.0)
We want to resample to half the original rate, so that’s
(resample <sound> (/ *sound-srate* stretch))
We need to stretch the length (Phase Vocoder) by a factor of 4, so the “map” needs to ramp up from 0 to 4 x .
(setf original-duration (get-duration 1))
(setf map (abs-env (pwlv 0 (* 4 original-duration) original-duration)))
or, more generally, we want “map” to ramp up to 2 x stretch-factor:
(setf original-duration (get-duration 1))
(setf map (abs-env (pwlv 0 (* 2 stretch original-duration) original-duration)))
But what if we want to stretch to half the frequency, and half the length?
In this case:
(setf stretch 0.5)
Resample to double the sample rate
(resample <sound> (/ *sound-srate* stretch))
Stretch the length by a factor of 0.25
(setf original-duration (get-duration 1))
(setf map (abs-env (pwlv 0 (* 0.5 stretch original-duration) original-duration)))
Note that the Phase Vocoder now stretches to half of “stretch factor”, whereas previously it was stretching to double the “stretch factor”.
So what is actually going on here?
When we stretch with resample, we change the speed (tempo and frequency) by “factor”.
Then when we stretch with the Phase Vocoder, we need to stretch by 1/factor to get back to the original length, and again to provide the desired tempo stretch. The required amount of stretch is 1/(factor x factor).
Now we can make a small modification to your code:
;nyquist plug-in
;version 4
;type process
;name "Inverted Speed Rate Change"
;restoresplits 0
;control factor "Stretch factor" float "" 1 0.1 10.0
(let ((slen (get-duration 1))
target
map)
(setf target (* slen factor factor))
(setf map (abs-env (pwlv 0 target slen)))
(resample (phasevocoder *track* map 2048 256 1)
(/ *sound-srate* factor)))