xyosc/vendor/github.com/mjibson/go-dsp/fft/radix2.go

200 lines
4.2 KiB
Go
Raw Normal View History

2025-01-16 21:47:06 +01:00
/*
* 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
import (
"math"
"runtime"
"sync"
)
var (
radix2Lock sync.RWMutex
radix2Factors = map[int][]complex128{
4: {complex(1, 0), complex(0, -1), complex(-1, 0), complex(0, 1)},
}
)
// EnsureRadix2Factors ensures that all radix 2 factors are computed for inputs
// of length input_len. This is used to precompute needed factors for known
// sizes. Generally should only be used for benchmarks.
func EnsureRadix2Factors(input_len int) {
getRadix2Factors(input_len)
}
func getRadix2Factors(input_len int) []complex128 {
radix2Lock.RLock()
if hasRadix2Factors(input_len) {
defer radix2Lock.RUnlock()
return radix2Factors[input_len]
}
radix2Lock.RUnlock()
radix2Lock.Lock()
defer radix2Lock.Unlock()
if !hasRadix2Factors(input_len) {
for i, p := 8, 4; i <= input_len; i, p = i<<1, i {
if radix2Factors[i] == nil {
radix2Factors[i] = make([]complex128, i)
for n, j := 0, 0; n < i; n, j = n+2, j+1 {
radix2Factors[i][n] = radix2Factors[p][j]
}
for n := 1; n < i; n += 2 {
sin, cos := math.Sincos(-2 * math.Pi / float64(i) * float64(n))
radix2Factors[i][n] = complex(cos, sin)
}
}
}
}
return radix2Factors[input_len]
}
func hasRadix2Factors(idx int) bool {
return radix2Factors[idx] != nil
}
type fft_work struct {
start, end int
}
// radix2FFT returns the FFT calculated using the radix-2 DIT Cooley-Tukey algorithm.
func radix2FFT(x []complex128) []complex128 {
lx := len(x)
factors := getRadix2Factors(lx)
t := make([]complex128, lx) // temp
r := reorderData(x)
var blocks, stage, s_2 int
jobs := make(chan *fft_work, lx)
wg := sync.WaitGroup{}
num_workers := worker_pool_size
if (num_workers) == 0 {
num_workers = runtime.GOMAXPROCS(0)
}
idx_diff := lx / num_workers
if idx_diff < 2 {
idx_diff = 2
}
worker := func() {
for work := range jobs {
for nb := work.start; nb < work.end; nb += stage {
if stage != 2 {
for j := 0; j < s_2; j++ {
idx := j + nb
idx2 := idx + s_2
ridx := r[idx]
w_n := r[idx2] * factors[blocks*j]
t[idx] = ridx + w_n
t[idx2] = ridx - w_n
}
} else {
n1 := nb + 1
rn := r[nb]
rn1 := r[n1]
t[nb] = rn + rn1
t[n1] = rn - rn1
}
}
wg.Done()
}
}
for i := 0; i < num_workers; i++ {
go worker()
}
defer close(jobs)
for stage = 2; stage <= lx; stage <<= 1 {
blocks = lx / stage
s_2 = stage / 2
for start, end := 0, stage; ; {
if end-start >= idx_diff || end == lx {
wg.Add(1)
jobs <- &fft_work{start, end}
if end == lx {
break
}
start = end
}
end += stage
}
wg.Wait()
r, t = t, r
}
return r
}
// reorderData returns a copy of x reordered for the DFT.
func reorderData(x []complex128) []complex128 {
lx := uint(len(x))
r := make([]complex128, lx)
s := log2(lx)
var n uint
for ; n < lx; n++ {
r[reverseBits(n, s)] = x[n]
}
return r
}
// log2 returns the log base 2 of v
// from: http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious
func log2(v uint) uint {
var r uint
for v >>= 1; v != 0; v >>= 1 {
r++
}
return r
}
// reverseBits returns the first s bits of v in reverse order
// from: http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious
func reverseBits(v, s uint) uint {
var r uint
// Since we aren't reversing all the bits in v (just the first s bits),
// we only need the first bit of v instead of a full copy.
r = v & 1
s--
for v >>= 1; v != 0; v >>= 1 {
r <<= 1
r |= v & 1
s--
}
return r << s
}