Forums > Jitter

Using jit.fft with Audio P1: Making a buffir~ Graphically

January 21, 2008 | 3:02 am

I feel like I get a lot from this message board but do not give nearly enough back. Hopefully I can change that. This is the first in a series of patches (2 or 3) showing how jit.fft can be used for offline fft calculations. I do not see much on here about using jit.fft with audio. Hopefully it will be useful to some :)

In making an arbitrary filter, one of the easiest ways is to put a frequency response into a buffer~ and apply it using index~ and some multiplication inside a pfft~. This patch shows how to get similar results using a different approach. Instead of doing an fft on the incoming audio, it uses the often overlooked buffir~ filter. Normally calculating the coefficients needed would be a hassle, which is where jit.fft comes in handy.

Drawing in the frequency graph writes into a 1D floating point jit.matrix object that is 256 "samples" wide. This matrix follows the shape of the graph, starting with low frequencies and working upwards. Before taking the fft, the frequency response data has to be mirrored and shifted so that a frequency of zero moves to the outer edges and high frequencies move to the middle. Jit.repos using boundmode 4 provides a simple way to mirror a matrix.

Now the frequency response is ready, but the jit.fft object also needs some phase values to do an inverse fft. In this case the jit.pack object essentially sets all the phase values to 0 for a linear phase filter. To go from frequency data back to the time domain, jit.fft performs an inverse fft on the data.

At this point jit.fft outputs all the coefficients we need, but not in the proper order. Previously the data was shifted so that "0" moved to the outer edges of the matrix. Now, the reverse is needed. To arrange the coefficients properly, a jit.repos object (boundmode 2) wraps the data into position. Because the filter is linear phase, it is symmetric and delayed. In this case the "0" point of the data is now in the middle.
Because the original frequency response going into jit.fft was on a scale from 0 to 1 and not 0 to 1/(length of the data), it has to be scaled down by a jit.op. Lastly, since the buffir~ object has a length limit of 256 samples, a jit.submatrix grabs the 256 values around the center.

#P window setfont "Sans Serif" 9.;
#P window linecount 1;
#P newex 762 452 36 9109513 cycle~;
#P newex 723 351 57 9109513 jit.normalize;
#P window setfont "Sans Serif" 10.;
#P comment 758 402 124 9109514 Graph of Coefficients;
#P comment 717 268 220 9109514 Second output here is for imaginary numbers;
#P comment 706 229 188 9109514 Second input here is for phase values;
#P comment 739 153 188 9109514 This code is only for display purposes;
#P comment 435 180 174 9109514 Frequency Response Ready for FFT;
#P window setfont "Sans Serif" 9.;
#P newex 657 518 33 9109513 *~ 10.;
#P newex 698 100 89 9109513 jit.op @op – @val 1;
#P newex 698 82 90 9109513 jit.op @op * @val 2;
#P newex 475 21 53 9109513 r to_matrix;
#P newex 492 621 55 9109513 s to_matrix;
#P newex 492 603 120 9109513 sprintf setcell %d 0 val %f;
#P newex 557 571 32 9109513 / 256.;
#P newex 557 551 35 9109513 !- 256.;
#P newex 523 551 27 9109513 / 2.;
#P newex 531 528 62 9109513 unpack 0 0 0;
#P newex 531 510 60 9109513 route mouse;
#P newex 428 104 46 9109513 jit.concat;
#P newex 464 79 218 9109513 jit.repos @offset_x 256 @mode 1 @boundmode 4;
#P button 428 22 15 0;
#P newex 428 42 110 9109513 jit.matrix 1 float32 256 1;
#P user spectroscope~ 657 539 300 100 50 0 0 0 1 1 1 0 0 0 0 0;
#X frgb 224 224 224;
#X brgb 255 255 255;
#X rgb2 0 0 0;
#X rgb3 243 204 204;
#X rgb4 255 0 0;
#X rgb5 184 184 184;
#X rgb6 0 0 0;
#X rgb7 0 0 0;
#X rgb8 255 255 255;
#X rgb9 255 0 0;
#X rgb10 255 191 0;
#X rgb11 0 191 127;
#X rgb12 127 0 127;
#X rgb13 0 0 0;
#X range 0. 1.;
#X domain 0. 22050.;
#X done;
#P newex 657 481 35 9109513 noise~;
#P toggle 916 492 15 0;
#P newex 916 507 28 9109513 dac~;
#P newex 656 405 75 9109513 jit.buffer~ IR 10;
#P newex 657 499 75 9109513 buffir~ IR 0 256;
#P user jit.pwindow 722 369 190 30 1 1 0 0 1 0;
#P newex 656 326 176 9109513 jit.submatrix @dim 256 1 @offset 128 0;
#P user jit.pwindow 427 143 190 30 1 1 0 0 1 0;
#P newex 656 308 99 9109513 jit.op @op / @val 512;
#P newex 656 290 218 9109513 jit.repos @offset_x 256 @mode 1 @boundmode 2;
#P newex 656 265 56 9109513 jit.unpack 2;
#P newex 656 229 46 9109513 jit.pack 2;
#P newex 656 247 146 9109513 jit.fft 2 float32 512 1 @inverse 1;
#P toggle 51 58 15 0;
#P newex 51 82 50 9109513 metro 100;
#P newex 51 109 40 9109513 t b b b;
#P user jit.pwindow 50 236 514 258 1 0 0 0 0 0;
#P newex 66 169 288 9109513 jit.gl.render lines @scale 2. 1. 1. @erase_color 1. 1. 1. 1. @ortho 2;
#P newex 66 139 45 9109513 t b erase;
#P newex 51 205 131 9109513 jit.matrix lines 4 char 512 256;
#P window linecount 2;
#P newex 698 118 261 9109513 jit.gl.graph lines @color 0. 0. 0. 1. @circpoints 1 @line_width 4 @antialias 1 @depth_enable 0 @blend_enable 1;
#P window setfont "Sans Serif" 14.;
#P window linecount 1;
#P comment 80 510 420 9109518 Draw a frequency response here (draw slowly or it will skip pixels);
#P window setfont "Sans Serif" 12.;
#P window linecount 2;
#P comment 756 491 140 9109516 Turn on the dac to see some filtered noise;
#P window setfont "Sans Serif" 10.;
#P window linecount 1;
#P comment 72 57 149 9109514 Show Frequency Response;
#P connect 10 0 9 0;
#P connect 9 0 8 0;
#P connect 8 0 4 0;
#P connect 4 0 7 0;
#P connect 8 1 5 0;
#P connect 5 0 6 0;
#P connect 5 1 6 0;
#P connect 36 0 26 0;
#P connect 26 0 25 0;
#P connect 36 0 25 0;
#P connect 25 0 28 0;
#P connect 28 0 16 0;
#P fasten 25 0 27 0 433 74 469 74;
#P connect 27 0 28 1;
#P connect 31 0 34 0;
#P connect 34 0 35 0;
#P connect 30 0 31 0;
#P connect 7 1 29 0;
#P connect 29 0 30 0;
#P connect 30 1 32 0;
#P connect 32 0 33 0;
#P connect 33 0 34 1;
#P fasten 28 0 12 0 433 135 661 135;
#P connect 12 0 11 0;
#P connect 11 0 13 0;
#P connect 13 0 14 0;
#P connect 14 0 15 0;
#P connect 15 0 17 0;
#P connect 17 0 20 0;
#P connect 23 0 19 0;
#P connect 19 0 39 0;
#P connect 39 0 24 0;
#P fasten 25 0 37 0 433 74 703 74;
#P connect 37 0 38 0;
#P connect 38 0 3 0;
#P connect 17 0 45 0;
#P connect 45 0 18 0;
#P connect 22 0 21 0;
#P window clipboard copycount 47;


January 21, 2008 | 8:06 am

Very nice!

You might want to [clip] the mouse input, to prevent negative
amplitudes.

So many directions to continue… put the spectrum into a feedback
system of matrix ops… or run a few of these from a jit.p.shiva…
or make a 2D matrix and you get a phase vocoder and plug in jit.bfg
or something…

On Jan 20, 2008, at 7:02 PM, Aaron Faulstich wrote:

>
> I feel like I get a lot from this message board but do not give
> nearly enough back. Hopefully I can change that. This is the first
> in a series of patches (2 or 3) showing how jit.fft can be used for
> offline fft calculations. I do not see much on here about using
> jit.fft with audio. Hopefully it will be useful to some :)
>
> In making an arbitrary filter, one of the easiest ways is to put a
> frequency response into a buffer~ and apply it using index~ and
> some multiplication inside a pfft~. This patch shows how to get
> similar results using a different approach. Instead of doing an fft
> on the incoming audio, it uses the often overlooked buffir~ filter.
> Normally calculating the coefficients needed would be a hassle,
> which is where jit.fft comes in handy.
>
> Drawing in the frequency graph writes into a 1D floating point
> jit.matrix object that is 256 "samples" wide. This
> matrix follows the shape of the graph, starting with low
> frequencies and working upwards. Before taking the fft, the
> frequency response data has to be mirrored and shifted so that a
> frequency of zero moves to the outer edges and high frequencies
> move to the middle. Jit.repos using boundmode 4 provides a simple
> way to mirror a matrix.
>
> Now the frequency response is ready, but the jit.fft object also
> needs some phase values to do an inverse fft. In this case the
> jit.pack object essentially sets all the phase values to 0 for a
> linear phase filter. To go from frequency data back to the time
> domain, jit.fft performs an inverse fft on the data.
>
> At this point jit.fft outputs all the coefficients we need, but not
> in the proper order. Previously the data was shifted so that
> "0" moved to the outer edges of the matrix. Now, the
> reverse is needed. To arrange the coefficients properly, a
> jit.repos object (boundmode 2) wraps the data into position.
> Because the filter is linear phase, it is symmetric and delayed. In
> this case the "0" point of the data is now in the middle.
> Because the original frequency response going into jit.fft was on a
> scale from 0 to 1 and not 0 to 1/(length of the data), it has to be
> scaled down by a jit.op. Lastly, since the buffir~ object has a
> length limit of 256 samples, a jit.submatrix grabs the 256 values
> around the center.
>


October 1, 2013 | 7:07 am

Hello SKRASMS!
I’don’t know if you will read this post but i have to try.
Your patch is great! it works very well.

What if i want the coefficients an amplitude flat response and an arbitrary phase response?
I’ve tried to put all ones on the left inlet of the jit.pack and the desired response in the second but all i’ve got is a phase running from 0rad to -400rad!

Any suggestion?


Viewing 3 posts - 1 through 3 (of 3 total)