mirror of
https://github.com/make-42/xyosc
synced 2025-01-19 11:07:35 +01:00
200 lines
4.2 KiB
Go
200 lines
4.2 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
|
||
|
|
||
|
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
|
||
|
}
|