[PD] pv/fft based realtime pitch shifter

Orm Finnendahl orm.finnendahl at selma.hfmdk-frankfurt.de
Sat Dec 23 14:45:30 CET 2023


Hi,

 as I couldn't find examples for a working fft/phase-vocoder based
realtime pitch shifter for pd online, I made one based on the
I07.phase.vocoder example of Miller Puckette, replacing the tabread4~
with a vd~.

I attach it, as some might have a use for it.

Happy Holidays to everybody!

--
Orm
-------------- next part --------------
#N canvas 34 2 637 529 12;
#X declare -stdpath ./;
#X floatatom 72 216 5 0 0 0 - \$0-transpo-set - 0;
#N canvas 894 2 754 709 fft-analysis 1;
#X obj 56 486 *~;
#X obj 23 486 *~;
#X obj 23 508 -~;
#X obj 172 484 *~;
#X obj 141 484 *~;
#X obj 141 506 +~;
#X obj 114 202 *~;
#X obj 83 202 *~;
#X obj 55 202 *~;
#X obj 24 202 *~;
#X obj 24 227 +~;
#X obj 128 394 *~;
#X obj 24 631 *~;
#X obj 239 439 rfft~;
#X obj 123 170 rfft~;
#X obj 23 573 rifft~;
#X obj 25 655 outlet~;
#X obj 98 394 *~;
#X obj 98 416 +~;
#X obj 129 227 -~;
#X obj 23 440 *~;
#X obj 56 441 *~;
#X obj 370 652 block~;
#X obj 24 358 +~ 1e-15;
#X obj 23 607 *~;
#X obj 56 607 tabreceive~ \$0-hann;
#X obj 127 655 expr 2/(3*$f1);
#X obj 555 545 loadbang;
#X msg 370 627 set \$1 4;
#X obj 98 440 q8_rsqrt~;
#N canvas 1981 2 1184 745 read-windows 0;
#X obj 132 551 *~;
#X obj 200 99 /;
#X obj 311 553 *~;
#X obj 132 260 bang~;
#X obj 132 415 line~;
#X obj 200 124 * 1000;
#X floatatom 678 231 7 0 0 0 - - - 0;
#X obj 678 207 *;
#X obj 798 128 + 69;
#X obj 798 153 mtof;
#X obj 798 180 / 440;
#X obj 713 207 t b f;
#X obj 131 584 outlet~;
#X obj 311 578 outlet~;
#X obj 329 522 tabreceive~ \$0-hann;
#X text 736 229 stretched window size (samples), f 16;
#X msg 955 108 set \$1;
#X floatatom 199 146 5 0 0 0 - - - 0;
#X obj 200 75 t f b;
#X obj 232 99 samplerate~;
#X obj 199 171 / 4;
#X text 10 466 "back" window 1/4 cycle behind "front" one, f 16;
#X text 230 171 computation period (msec) for overlap of 4;
#X text 252 145 msec in a window;
#X text 969 37 reflect control changes;
#X text 961 59 in main window.;
#X obj 132 515 vd~ \$0-delay;
#X obj 310 495 vd~ \$0-delay;
#X floatatom 798 205 5 0 0 0 - - - 0;
#X floatatom 211 204 5 0 0 0 - - - 0;
#X obj 678 280 /;
#X obj 678 305 * 1000;
#X floatatom 677 327 5 0 0 0 - - - 0;
#X obj 678 256 t f b;
#X obj 710 280 samplerate~;
#X text 726 312 stretched window size (msec);
#X obj 676 485 route 0 1;
#X obj 677 435 > 0;
#X obj 676 460 pack 0 0 0;
#X listbox 768 665 20 0 0 0 - - - 0;
#X obj 677 516 expr $f2 - $f1;
#X obj 801 515 -;
#X obj 132 291 list;
#X obj 132 316 list trim;
#X obj 132 347 pack f f f;
#X obj 152 181 bng 20 250 50 0 empty empty empty 0 -10 0 12 #fcfcfc #000000 #000000;
#X obj 677 355 / 4;
#X floatatom 677 380 5 0 0 0 - - - 0;
#X obj 677 404 t f f;
#X obj 133 481 +~ 0;
#X msg 132 387 \$1 \, \$2 \$3;
#X obj 677 544 + 0;
#X obj 801 540 + 0;
#X msg 677 571 0 \$1;
#X msg 801 568 \$1 0;
#X obj 798 103 * 0.01;
#X obj 200 51 r \$0-window-size;
#X obj 798 78 r \$0-transpo;
#X obj 956 133 s \$0-transpo-set;
#X obj 955 82 r \$0-transpo;
#X text 337 52 Read two windows from the delay line \, one 1/4 ahead of the other., f 45;
#X text 673 605 pitch down;
#X text 804 602 pitch up;
#X text 736 434 transpose down/up?;
#X text 718 354 divide by overlap;
#X text 769 695 vd~ params;
#X obj 673 155 r \$0-window-size;
#X obj 673 180 pipe 10;
#X connect 0 0 12 0;
#X connect 1 0 5 0;
#X connect 2 0 13 0;
#X connect 3 0 42 0;
#X connect 4 0 49 0;
#X connect 4 0 27 0;
#X connect 5 0 17 0;
#X connect 6 0 33 0;
#X connect 7 0 6 0;
#X connect 8 0 9 0;
#X connect 9 0 10 0;
#X connect 10 0 11 0;
#X connect 10 0 28 0;
#X connect 11 0 7 0;
#X connect 11 1 7 1;
#X connect 14 0 2 1;
#X connect 14 0 0 1;
#X connect 16 0 58 0;
#X connect 17 0 20 0;
#X connect 18 0 1 0;
#X connect 18 1 19 0;
#X connect 19 0 1 1;
#X connect 20 0 29 0;
#X connect 20 0 44 2;
#X connect 26 0 0 0;
#X connect 27 0 2 0;
#X connect 29 0 37 1;
#X connect 29 0 38 2;
#X connect 30 0 31 0;
#X connect 31 0 32 0;
#X connect 32 0 46 0;
#X connect 33 0 30 0;
#X connect 33 1 34 0;
#X connect 34 0 30 1;
#X connect 36 0 40 0;
#X connect 36 1 41 0;
#X connect 37 0 38 0;
#X connect 38 0 36 0;
#X connect 40 0 51 0;
#X connect 41 0 52 0;
#X connect 42 0 43 0;
#X connect 43 0 44 0;
#X connect 44 0 50 0;
#X connect 45 0 29 0;
#X connect 46 0 47 0;
#X connect 46 0 49 1;
#X connect 47 0 48 0;
#X connect 48 0 37 0;
#X connect 48 1 38 1;
#X connect 49 0 26 0;
#X connect 50 0 4 0;
#X connect 51 0 53 0;
#X connect 52 0 54 0;
#X connect 53 0 39 0;
#X connect 53 0 42 1;
#X connect 54 0 39 0;
#X connect 54 0 42 1;
#X connect 55 0 8 0;
#X connect 56 0 18 0;
#X connect 57 0 55 0;
#X connect 59 0 16 0;
#X connect 66 0 67 0;
#X connect 67 0 7 0;
#X restore 123 142 pd read-windows;
#X text 277 14 recall previous output amplitude. Its phase will be added to the phase difference we measure from two windows in the the recorded sound.;
#X obj 126 78 *~;
#X obj 99 78 *~;
#X obj 99 100 +~;
#X obj 164 103 q8_rsqrt~;
#X obj 164 80 +~ 1e-20;
#X obj 83 128 *~;
#X obj 24 127 *~;
#X obj 182 299 r lock;
#X obj 34 254 lrshift~ 1;
#X obj 29 278 lrshift~ -1;
#X obj 146 254 lrshift~ 1;
#X obj 138 278 lrshift~ -1;
#X obj 42 309 *~;
#X obj 164 321 *~;
#X obj 24 334 +~;
#X obj 130 321 +~;
#X text 252 75 divide by the magnitude to make a unit-magnitude complex amplitude (phase only). The 1e-20 is to prevent overflows. q8_rsqrt~ is reciprocal square root.;
#X text 252 174 Take FT of the window in back. Multiply its conjugate by the normalized previous output. The result has the magnitude of the input sound and phase (previous output phase) minus (back window phase).;
#X text 254 379 Normalize again \, this time taking care to salt each channel with 1e-15 so that we get a unit complex number even if everything was zero heretofore.;
#X text 287 435 Now take the FT of the forward window and multiply it by the unit complex number from above. The magnitude will be that of the forward window and the phase will be the previous output phase plus the phase difference between the two analysis windows -- except that if "lock" is on \, they will be modified to agree progressively better with the inter-channel phase relationships of the input.;
#X text 254 251 If "lock" is on \, encourage neighboring channels to stay in phase by adding the two neighboring complex amplitudes. The result will tend toward the channel with the strongest amplitude. If the phase relationships between channels in the output and those in the input are in parallel \, then neighboring channels of the quotient will all have the same phase and this will not change any phases. (lrshift shifts the signal to the left or right depending on its argument.);
#X text 264 135 Read two windows \, one 1/4 length behind the other \, of the input sound \, with Hann window function (see inside).;
#X text 341 566 'set' message to block allows variable size, f 22;
#X obj 556 601 f \$0;
#X msg 557 629 \; \$1-window-size 8192 \; \$1-transpo 0;
#X obj 370 603 r \$0-window-size;
#X obj 127 633 r \$0-window-size;
#X obj 26 14 tabreceive~ \$0-prev-real;
#X obj 84 38 tabreceive~ \$0-prev-imag;
#X obj 141 541 tabsend~ \$0-prev-imag;
#X obj 140 575 tabsend~ \$0-prev-real;
#X connect 0 0 2 1;
#X connect 1 0 2 0;
#X connect 2 0 15 0;
#X connect 2 0 62 0;
#X connect 3 0 5 1;
#X connect 4 0 5 0;
#X connect 5 0 15 1;
#X connect 5 0 61 0;
#X connect 6 0 19 1;
#X connect 7 0 19 0;
#X connect 8 0 10 1;
#X connect 9 0 10 0;
#X connect 10 0 41 0;
#X connect 10 0 40 0;
#X connect 10 0 46 0;
#X connect 11 0 18 1;
#X connect 12 0 16 0;
#X connect 13 0 1 1;
#X connect 13 0 3 1;
#X connect 13 1 0 1;
#X connect 13 1 4 1;
#X connect 14 0 9 1;
#X connect 14 0 7 1;
#X connect 14 1 6 1;
#X connect 14 1 8 1;
#X connect 15 0 24 0;
#X connect 17 0 18 0;
#X connect 18 0 29 0;
#X connect 19 0 42 0;
#X connect 19 0 43 0;
#X connect 19 0 47 0;
#X connect 20 0 1 0;
#X connect 20 0 4 0;
#X connect 21 0 0 0;
#X connect 21 0 3 0;
#X connect 23 0 17 1;
#X connect 23 0 17 0;
#X connect 23 0 20 0;
#X connect 24 0 12 0;
#X connect 25 0 24 1;
#X connect 26 0 12 1;
#X connect 27 0 55 0;
#X connect 28 0 22 0;
#X connect 29 0 20 1;
#X connect 29 0 21 1;
#X connect 30 0 14 0;
#X connect 30 1 13 0;
#X connect 32 0 34 1;
#X connect 33 0 34 0;
#X connect 34 0 36 0;
#X connect 35 0 37 1;
#X connect 35 0 38 1;
#X connect 36 0 35 0;
#X connect 37 0 8 0;
#X connect 37 0 7 0;
#X connect 38 0 9 0;
#X connect 38 0 6 0;
#X connect 39 0 44 1;
#X connect 39 0 45 1;
#X connect 40 0 44 0;
#X connect 41 0 44 0;
#X connect 42 0 45 0;
#X connect 43 0 45 0;
#X connect 44 0 46 1;
#X connect 45 0 47 1;
#X connect 46 0 23 0;
#X connect 47 0 11 0;
#X connect 47 0 11 1;
#X connect 47 0 21 0;
#X connect 55 0 56 0;
#X connect 57 0 28 0;
#X connect 58 0 26 0;
#X connect 59 0 33 1;
#X connect 59 0 33 0;
#X connect 59 0 38 0;
#X connect 60 0 32 1;
#X connect 60 0 32 0;
#X connect 60 0 37 0;
#X restore 69 396 pd fft-analysis;
#N canvas 260 23 531 738 phase-tables 0;
#N canvas 0 0 450 300 (subpatch) 0;
#X array \$0-prev-imag 4096 float 0;
#X coords 0 1000 4096 -1000 400 300 1 0 0;
#X restore 67 395 graph;
#N canvas 0 0 450 300 (subpatch) 0;
#X array \$0-prev-real 4096 float 0;
#X coords 0 500 4096 -500 400 300 1 0 0;
#X restore 67 40 graph;
#X restore 69 347 pd phase-tables;
#X text 71 195 in cents;
#X obj 70 434 output~;
#N canvas 385 188 595 536 hann-window 0;
#N canvas 0 0 450 300 (subpatch) 0;
#X array \$0-hann 16384 float 0;
#X coords 0 1 16383 0 300 100 1;
#X restore 148 395 graph;
#X obj 365 185 osc~;
#X obj 365 210 *~ -0.5;
#X obj 365 234 +~ 0.5;
#X obj 348 277 tabwrite~ \$0-hann;
#X obj 195 238 /;
#X obj 213 206 samplerate~;
#X obj 71 267 swap;
#X obj 71 291 /;
#X obj 206 266 * 1000;
#X obj 124 149 t f b f;
#X msg 213 156 resize \$1;
#X obj 213 180 s \$0-hann;
#X msg 390 155 0;
#X obj 317 151 t f b;
#X text 94 21 calculate Hann window table (variable window size) and constants window-hz (fundamental frequency of analysis) \, window-sec and window-msec (analysis window size in seconds and msec)., f 56;
#X obj 124 114 r \$0-window-size;
#X obj 317 123 r \$0-window-hz;
#X obj 71 315 s \$0-window-hz;
#X obj 195 316 s \$0-window-sec;
#X obj 206 293 s \$0-window-msec;
#X connect 1 0 2 0;
#X connect 2 0 3 0;
#X connect 3 0 4 0;
#X connect 5 0 9 0;
#X connect 5 0 19 0;
#X connect 6 0 5 1;
#X connect 6 0 7 1;
#X connect 7 0 8 0;
#X connect 7 1 8 1;
#X connect 8 0 18 0;
#X connect 9 0 20 0;
#X connect 10 0 5 0;
#X connect 10 0 7 0;
#X connect 10 1 6 0;
#X connect 10 2 11 0;
#X connect 11 0 12 0;
#X connect 13 0 1 1;
#X connect 14 0 1 0;
#X connect 14 1 4 0;
#X connect 14 1 13 0;
#X connect 16 0 10 0;
#X connect 17 0 14 0;
#X restore 69 371 pd hann-window;
#X obj 72 297 tgl 15 0 empty empty empty 0 -6 0 8 #fcfcfc #000000 #000000 0 1;
#X obj 72 317 s lock;
#X text 72 178 detune;
#X msg 310 301 512;
#X msg 316 324 1024;
#X msg 321 347 2048;
#X text 307 259 window size \,;
#X text 307 274 samples;
#X obj 129 217 bng 19 250 50 0 no-detune empty empty 0 -6 0 8 #dfdfdf #000000 #000000;
#X text 345 300 <= set;
#X obj 469 21 declare -stdpath ./;
#X msg 343 419 16384;
#X msg 337 395 8192;
#X obj 299 168 adc~;
#X obj 307 215 delwrite~ \$0-delay 1000;
#X obj 310 445 s \$0-window-size;
#X obj 72 239 s \$0-transpo;
#X msg 328 371 4096;
#X text 66 91 simply replacing the tabread4~ with a vd~. In order;
#X text 65 114 to get a decent frequency resolution \, the window size;
#X text 70 21 PHASE VOCODER based real time pitch shifter;
#X text 68 68 Based on I07.phase.vocoder by Miller Puckette;
#X floatatom 309 471 5 0 0 0 - \$0-window-size - 0;
#X text 119 296 enable phase locking;
#X text 356 472 window size;
#X text 66 136 probably should be at least 4096;
#X connect 0 0 22 0;
#X connect 1 0 4 1;
#X connect 1 0 4 0;
#X connect 6 0 7 0;
#X connect 9 0 21 0;
#X connect 10 0 21 0;
#X connect 11 0 21 0;
#X connect 17 0 21 0;
#X connect 18 0 21 0;
#X connect 19 0 20 0;
#X connect 19 1 20 0;
#X connect 23 0 21 0;


More information about the Pd-list mailing list