HTML5 Creating an Audio Visulizer (FFT)

c023-DeV

Member
Heya!
I'm trying to set up an audio visulizer to demo some of my music on my website. I've found a visulizer on the marketplace but it doesn't support HTML5 and most information I can find regarding the Fast Fourier Transform is either pure math or on how to use pre-built \ integrated functions in other engines and I can't even look into those functions.

So I'm a bit lost right now.

Does anyone know if it's even possible to make an FFT script or function using pure GML?

If so, can you maybe point me in some direction or code example that I can translate to GML?
 

c023-DeV

Member
By Googling "Fast Fourier Transform implementation python", I found this page, containing both the math and an implementation in python.
Hey,
thanks for the example.
To be honest, this whole thing just keeps on getting more and more intimidating since the linked article says:

"We still haven’t come close to the speed at which the numpy library computes the Fourier Transform. This is because the FFTPACK algorithm behind numpy’s fft is a Fortran implementation which has received years of tweaks and optimizations."

... it's not that I'd want to make the best and fastest implementation, but that scares me.

Also, the example makes heavy use of some "np.*" lib and I have no idea what those functions do.

I'll try to pick this appart, but I do need help...

So this is the whole function I guess:

Python:
def fft_v(x):
    x = np.asarray(x, dtype=float)
    N = x.shape[0]
    if np.log2(N) % 1 > 0:
        raise ValueError("must be a power of 2")
        
    N_min = min(N, 2)
    
    n = np.arange(N_min)
    k = n[:, None]
    M = np.exp(-2j * np.pi * n * k / N_min)
    X = np.dot(M, x.reshape((N_min, -1)))
while X.shape[0] < N:
        X_even = X[:, :int(X.shape[1] / 2)]
        X_odd = X[:, int(X.shape[1] / 2):]
        terms = np.exp(-1j * np.pi * np.arange(X.shape[0])
                        / X.shape[0])[:, None]
        X = np.vstack([X_even + terms * X_odd,
                       X_even - terms * X_odd])
return X.ravel()
I'll boot up my notebook in a bit, gotta wait for some solar charge (I live in a van so I my screentime is limited by sun)...

Maybe someone can tell me what the np.* in that code does?

I also found some js version here: JS FFT
From this site: Benchmarking FFT in JS
 

rytan451

Member
So np.asarray converts the input (x) to a numpy array, which has loads of utility functions.

On line 2, x is converted to an array.
On line 3, N is assigned as the length of the array along the x axis.
On line 4 and 5, they're checking if the length is a power of two. Another way of doing it is this:
GML:
if (((N - 1) & N) != 0) {
  throw "Must be a power of 2"; // Before GMS2.3, this would be `show_error("Must be a power of 2", true)`
}
On line 7, n is assigned as an array range. This is the 1-dimensional array containing [0, 1, 2, ... N_min - 1].
On line 8, k is assigned as a 2-dimensional array. In y index 0, it has a duplicate of n. However, its length along the y axis is 1. In GML, this can be achieved this way:
GML:
k = [n];
/* 
Before GMS2.3, this would be:
k = 0;
k[0] = n;
*/
On line 9, it's just a lot of different math. First, each element of k is squared. Then, it's multiplied by -2i * pi / N_min (a complex number). Then, the exponential function is applied to each element of k. Finally, the result is assigned to M. Since the input for the exponential function is complex, you'll have to write your own complex exponential function.
On line 10, it's performing matrix multiplication between the Nx1 matrix M and the Nx1 matrix that is the transposition of x. This will output a NxN matrix.

Actually, all the code is readily understandable if you look at the numpy documentation.
 
Top