Phase vocoder in RNBO — is a hop size smaller than the FFT size possible?

ben_parm's icon

I'm porting a Max for Live device to RNBO for a Raspberry Pi build. The original uses gizmo~ inside pfft~ for pitch shifting, and since RNBO has neither object I've been implementing a phase vocoder in codebox~ following Laroche & Dolson's peak-shifting method.

The algorithm itself works. On a bin-centred sine tone it shifts accurately at every ratio I need (0.5, 1.5, 2, 3, 4), measuring within about a cent with a single clean peak and no sidebands. On real instruments and on granular buffer playback it's unusable, a constant fluttering, at a musical rate rather than at the frame rate or any multiple of it.

I believe the cause is analysis time resolution, and I'd like to know whether there's a way around it that I've missed.

As far as I can tell, fft~ streams one bin per sample, so a 2048-point FFT necessarily takes 2048 samples to emit a frame and the hop is locked to the FFT size. I'm getting overlap by running four fft~/ifft~ chains in parallel with phase offsets of 0/512/1024/1536, which does give a properly overlapped output sum. But each individual chain still analyses on a 46 ms window, so nothing faster than that is resolved, and gizmo~ inside pfft~ at overlap 4 has a hop of N/4.

Things I've already tried:

  • fft~ 2048 512 0 — no change, consistent with framesize being clamped to the FFT size

  • dropping to 512-point FFTs with four chains at 64-sample offsets, for an 11.6 ms window, improves nothing audibly and costs frequency resolution

  • one chain versus four sounds the same, so it isn't the chains disagreeing with each other

  • measuring the peak set frame to frame on real instrument: only about 22% of detected peaks persist at the same bin between frames, and the count swings between roughly 50 and 150 per frame, so the spectral partitioning is being redrawn 21 times a second. Adding a prominence test cut the peak count to around 6 and raised persistence to 42%, but didn't change what I hear.

My questions:

  1. Is there any way to get a genuine hop smaller than the FFT size in RNBO, an fft~ argument or attribute I've missed, or another approach entirely?

  2. fftstream/ifftstream in codebox~ looked like the right tool but fails with No option of name: win_bufname on RNBO 1.4.5, which I understand was supposed to be fixed in 1.4.4. Is that expected, and is there a working example of them anywhere?

  3. Has anyone got a phase vocoder working well in RNBO? I couldn't find one published.

  4. Is a gizmo~ equivalent, or pfft~-style overlapped framing, on the roadmap?

Happy to share the codebox~ source and measurements if useful. Running RNBO 1.4.5 in Max 9, targeting a Pi with a Pisound Micro.