mirror of
https://github.com/make-42/xyosc
synced 2025-01-19 02:57:34 +01:00
225 lines
5.0 KiB
Go
225 lines
5.0 KiB
Go
/*
|
||
* Copyright (c) 2011 Matt Jibson <matt.jibson@gmail.com>
|
||
*
|
||
* Permission to use, copy, modify, and distribute this software for any
|
||
* purpose with or without fee is hereby granted, provided that the above
|
||
* copyright notice and this permission notice appear in all copies.
|
||
*
|
||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||
*/
|
||
|
||
// Package fft provides forward and inverse fast Fourier transform functions.
|
||
package fft
|
||
|
||
import (
|
||
"github.com/mjibson/go-dsp/dsputils"
|
||
)
|
||
|
||
// FFTReal returns the forward FFT of the real-valued slice.
|
||
func FFTReal(x []float64) []complex128 {
|
||
return FFT(dsputils.ToComplex(x))
|
||
}
|
||
|
||
// IFFTReal returns the inverse FFT of the real-valued slice.
|
||
func IFFTReal(x []float64) []complex128 {
|
||
return IFFT(dsputils.ToComplex(x))
|
||
}
|
||
|
||
// IFFT returns the inverse FFT of the complex-valued slice.
|
||
func IFFT(x []complex128) []complex128 {
|
||
lx := len(x)
|
||
r := make([]complex128, lx)
|
||
|
||
// Reverse inputs, which is calculated with modulo N, hence x[0] as an outlier
|
||
r[0] = x[0]
|
||
for i := 1; i < lx; i++ {
|
||
r[i] = x[lx-i]
|
||
}
|
||
|
||
r = FFT(r)
|
||
|
||
N := complex(float64(lx), 0)
|
||
for n := range r {
|
||
r[n] /= N
|
||
}
|
||
return r
|
||
}
|
||
|
||
// Convolve returns the convolution of x ∗ y.
|
||
func Convolve(x, y []complex128) []complex128 {
|
||
if len(x) != len(y) {
|
||
panic("arrays not of equal size")
|
||
}
|
||
|
||
fft_x := FFT(x)
|
||
fft_y := FFT(y)
|
||
|
||
r := make([]complex128, len(x))
|
||
for i := 0; i < len(r); i++ {
|
||
r[i] = fft_x[i] * fft_y[i]
|
||
}
|
||
|
||
return IFFT(r)
|
||
}
|
||
|
||
// FFT returns the forward FFT of the complex-valued slice.
|
||
func FFT(x []complex128) []complex128 {
|
||
lx := len(x)
|
||
|
||
// todo: non-hack handling length <= 1 cases
|
||
if lx <= 1 {
|
||
r := make([]complex128, lx)
|
||
copy(r, x)
|
||
return r
|
||
}
|
||
|
||
if dsputils.IsPowerOf2(lx) {
|
||
return radix2FFT(x)
|
||
}
|
||
|
||
return bluesteinFFT(x)
|
||
}
|
||
|
||
var (
|
||
worker_pool_size = 0
|
||
)
|
||
|
||
// SetWorkerPoolSize sets the number of workers during FFT computation on multicore systems.
|
||
// If n is 0 (the default), then GOMAXPROCS workers will be created.
|
||
func SetWorkerPoolSize(n int) {
|
||
if n < 0 {
|
||
n = 0
|
||
}
|
||
|
||
worker_pool_size = n
|
||
}
|
||
|
||
// FFT2Real returns the 2-dimensional, forward FFT of the real-valued matrix.
|
||
func FFT2Real(x [][]float64) [][]complex128 {
|
||
return FFT2(dsputils.ToComplex2(x))
|
||
}
|
||
|
||
// FFT2 returns the 2-dimensional, forward FFT of the complex-valued matrix.
|
||
func FFT2(x [][]complex128) [][]complex128 {
|
||
return computeFFT2(x, FFT)
|
||
}
|
||
|
||
// IFFT2Real returns the 2-dimensional, inverse FFT of the real-valued matrix.
|
||
func IFFT2Real(x [][]float64) [][]complex128 {
|
||
return IFFT2(dsputils.ToComplex2(x))
|
||
}
|
||
|
||
// IFFT2 returns the 2-dimensional, inverse FFT of the complex-valued matrix.
|
||
func IFFT2(x [][]complex128) [][]complex128 {
|
||
return computeFFT2(x, IFFT)
|
||
}
|
||
|
||
func computeFFT2(x [][]complex128, fftFunc func([]complex128) []complex128) [][]complex128 {
|
||
rows := len(x)
|
||
if rows == 0 {
|
||
panic("empty input array")
|
||
}
|
||
|
||
cols := len(x[0])
|
||
r := make([][]complex128, rows)
|
||
for i := 0; i < rows; i++ {
|
||
if len(x[i]) != cols {
|
||
panic("ragged input array")
|
||
}
|
||
r[i] = make([]complex128, cols)
|
||
}
|
||
|
||
for i := 0; i < cols; i++ {
|
||
t := make([]complex128, rows)
|
||
for j := 0; j < rows; j++ {
|
||
t[j] = x[j][i]
|
||
}
|
||
|
||
for n, v := range fftFunc(t) {
|
||
r[n][i] = v
|
||
}
|
||
}
|
||
|
||
for n, v := range r {
|
||
r[n] = fftFunc(v)
|
||
}
|
||
|
||
return r
|
||
}
|
||
|
||
// FFTN returns the forward FFT of the matrix m, computed in all N dimensions.
|
||
func FFTN(m *dsputils.Matrix) *dsputils.Matrix {
|
||
return computeFFTN(m, FFT)
|
||
}
|
||
|
||
// IFFTN returns the forward FFT of the matrix m, computed in all N dimensions.
|
||
func IFFTN(m *dsputils.Matrix) *dsputils.Matrix {
|
||
return computeFFTN(m, IFFT)
|
||
}
|
||
|
||
func computeFFTN(m *dsputils.Matrix, fftFunc func([]complex128) []complex128) *dsputils.Matrix {
|
||
dims := m.Dimensions()
|
||
t := m.Copy()
|
||
r := dsputils.MakeEmptyMatrix(dims)
|
||
|
||
for n := range dims {
|
||
dims[n] -= 1
|
||
}
|
||
|
||
for n := range dims {
|
||
d := make([]int, len(dims))
|
||
copy(d, dims)
|
||
d[n] = -1
|
||
|
||
for {
|
||
r.SetDim(fftFunc(t.Dim(d)), d)
|
||
|
||
if !decrDim(d, dims) {
|
||
break
|
||
}
|
||
}
|
||
|
||
r, t = t, r
|
||
}
|
||
|
||
return t
|
||
}
|
||
|
||
// decrDim decrements an element of x by 1, skipping all -1s, and wrapping up to d.
|
||
// If a value is 0, it will be reset to its correspending value in d, and will carry one from the next non -1 value to the right.
|
||
// Returns true if decremented, else false.
|
||
func decrDim(x, d []int) bool {
|
||
for n, v := range x {
|
||
if v == -1 {
|
||
continue
|
||
} else if v == 0 {
|
||
i := n
|
||
// find the next element to decrement
|
||
for ; i < len(x); i++ {
|
||
if x[i] == -1 {
|
||
continue
|
||
} else if x[i] == 0 {
|
||
x[i] = d[i]
|
||
} else {
|
||
x[i] -= 1
|
||
return true
|
||
}
|
||
}
|
||
|
||
// no decrement
|
||
return false
|
||
} else {
|
||
x[n] -= 1
|
||
return true
|
||
}
|
||
}
|
||
|
||
return false
|
||
}
|