func XCorrFFT(x1, x2 []float64, circular bool) []float64 { // zero padding. ftlength := len(x1) if !circular { length := len(x1) * 2 var i uint32 = 1 for ftlength < length { ftlength = 1 << i i++ } } datax1 := make([]complex128, ftlength) datax2 := make([]complex128, ftlength) for i := 0; i < len(x1); i++ { datax1[i] = complex(x2[i%len(x2)], 0) datax2[i] = complex(x1[i%len(x1)], 0) } v1 := fft.FFT(datax1) v2 := fft.FFT(datax2) temp := []complex128{} for i := 0; i < len(v1); i++ { v := v1[i] * cmplx.Conj(v2[i]) temp = append(temp, v) } v3 := fft.IFFT(temp) res := []float64{} for i := 0; i < len(x1); i++ { res = append(res, real(v3[i])) } return res }
// This basically takes the inner product of the FFT of the path treated as a // complex signal and a linear function with positive values for // high frequencies and negative values for low frequencies. func ComputeFrequencyRating(points [][2]float64) float64 { // First, find the "center" of the path var centerX, centerY float64 for i := 0; i < len(points); i++ { centerX += points[i][0] centerY += points[i][1] } centerX /= float64(len(points)) centerY /= float64(len(points)) // Estimate the scale of the path using the mean distance from the center var scale float64 = 0 for i := 0; i < len(points); i++ { var xDistance = points[i][0] - centerX var yDistance = points[i][1] - centerY scale += math.Sqrt(xDistance*xDistance + yDistance*yDistance) } scale /= float64(len(points)) // Now turn the path into a complex signal // This is distance-naive var signal []complex128 for i := 0; i < len(points); i++ { signal = append(signal, complex((points[i][0]-centerX)/scale, (points[i][0]-centerY)/scale)) } // Now take the FFT var signalFFT = fft.FFT(signal) // Now add up the frequency magnitudes, weighted by frequency var frequencyRating float64 = 0 for i := 0; i < len(signalFFT); i++ { var frequencyMagnitude float64 var frequencyWeight float64 // Use a simple linear function ranging from -1 to 1 frequencyWeight = 2 * float64(i) / (float64(len(signalFFT)-1) - 1) // The frequency magnitude is the complex magnitude var realAmplitude = real(signalFFT[i]) var imaginaryAmplitude = imag(signalFFT[i]) frequencyMagnitude = math.Sqrt(realAmplitude*realAmplitude + imaginaryAmplitude*imaginaryAmplitude) // Add it to the total frequencyRating += frequencyMagnitude * frequencyWeight } return frequencyRating }
// TODO - clean up a lot. func NewCQKernel(params CQParams) *CQKernel { // Constructor p := Properties{} p.sampleRate = params.sampleRate p.maxFrequency = params.maxFrequency p.binsPerOctave = params.binsPerOctave // GenerateKernel q := params.q atomHopFactor := params.atomHopFactor thresh := params.threshold bpo := params.binsPerOctave p.minFrequency = float64(math.Pow(2, 1/float64(bpo)) * float64(params.maxFrequency) / 2.0) p.Q = q / (math.Pow(2, 1.0/float64(bpo)) - 1.0) maxNK := float64(int(math.Floor(p.Q*p.sampleRate/p.minFrequency + 0.5))) minNK := float64(int(math.Floor(p.Q*p.sampleRate/ (p.minFrequency*math.Pow(2.0, (float64(bpo)-1.0)/float64(bpo))) + 0.5))) if minNK == 0 || maxNK == 0 { panic("Kernal minNK or maxNK is 0, can't make kernel") } p.atomSpacing = round(minNK*atomHopFactor + 0.5) p.firstCentre = p.atomSpacing * roundUp(math.Ceil(maxNK/2.0)/float64(p.atomSpacing)) p.fftSize = nextPowerOf2(p.firstCentre + roundUp(maxNK/2.0)) p.atomsPerFrame = roundDown(1.0 + (float64(p.fftSize)-math.Ceil(maxNK/2.0)-float64(p.firstCentre))/float64(p.atomSpacing)) if DEBUG { fmt.Printf("atomsPerFrame = %v (q = %v, Q = %v, atomHopFactor = %v, atomSpacing = %v, fftSize = %v, maxNK = %v, firstCentre = %v)\n", p.atomsPerFrame, q, p.Q, atomHopFactor, p.atomSpacing, p.fftSize, maxNK, p.firstCentre) } p.lastCentre = p.firstCentre + (p.atomsPerFrame-1)*p.atomSpacing p.fftHop = (p.lastCentre + p.atomSpacing) - p.firstCentre if DEBUG { fmt.Printf("fftHop = %v\n", p.fftHop) } dataSize := p.binsPerOctave * p.atomsPerFrame kernel := Kernel{ make([]int, 0, dataSize), make([][]complex128, 0, dataSize), } for k := 1; k <= p.binsPerOctave; k++ { nk := int(p.Q*p.sampleRate/(p.minFrequency*math.Pow(2, ((float64(k)-1.0)/float64(bpo)))) + 0.5) win := makeWindow(params.window, nk) fk := float64(p.minFrequency * math.Pow(2, ((float64(k)-1.0)/float64(bpo)))) cmplxs := make([]complex128, nk, nk) for i := 0; i < nk; i++ { arg := (2.0 * math.Pi * fk * float64(i)) / p.sampleRate cmplxs[i] = cmplx.Rect(win[i], arg) } atomOffset := p.firstCentre - roundUp(float64(nk)/2.0) for i := 0; i < p.atomsPerFrame; i++ { shift := atomOffset + (i * p.atomSpacing) cin := make([]complex128, p.fftSize, p.fftSize) for j := 0; j < nk; j++ { cin[j+shift] = cmplxs[j] } cout := fft.FFT(cin) // Keep this dense for the moment (until after normalisation calculations) for j := 0; j < p.fftSize; j++ { if cmplx.Abs(cout[j]) < thresh { cout[j] = complex(0, 0) } else { cout[j] = complexTimes(cout[j], 1.0/float64(p.fftSize)) } } kernel.origin = append(kernel.origin, 0) kernel.data = append(kernel.data, cout) } } if DEBUG { fmt.Printf("size = %v * %v (fft size = %v)\n", len(kernel.data), len(kernel.data[0]), p.fftSize) } // finalizeKernel // calculate weight for normalisation wx1 := maxidx(kernel.data[0]) wx2 := maxidx(kernel.data[len(kernel.data)-1]) subset := make([][]complex128, len(kernel.data), len(kernel.data)) for i := 0; i < len(kernel.data); i++ { subset[i] = make([]complex128, 0, wx2-wx1+1) } for j := wx1; j <= wx2; j++ { for i := 0; i < len(kernel.data); i++ { subset[i] = append(subset[i], kernel.data[i][j]) } } // Massive hack - precalculate above instead :( nrows, ncols := len(subset), len(subset[0]) square := make([][]complex128, ncols, ncols) // conjugate transpose of subset * subset for i := 0; i < ncols; i++ { square[i] = make([]complex128, ncols, ncols) } for j := 0; j < ncols; j++ { for i := 0; i < ncols; i++ { v := complex(0, 0) for k := 0; k < nrows; k++ { v += subset[k][i] * cmplx.Conj(subset[k][j]) } square[i][j] = v } } wK := []float64{} for i := int(1.0/q + 0.5); i < ncols-int(1.0/q+0.5)-2; i++ { wK = append(wK, cmplx.Abs(square[i][i])) } weight := float64(p.fftHop) / float64(p.fftSize) if len(wK) > 0 { weight /= mean(wK) } weight = math.Sqrt(weight) if DEBUG { fmt.Printf("weight = %v (from %v elements in wK, ncols = %v, q = %v)\n", weight, len(wK), ncols, q) } // apply normalisation weight, make sparse, and store conjugate // (we use the adjoint or conjugate transpose of the kernel matrix // for the forward transform, the plain kernel for the inverse // which we expect to be less common) sk := Kernel{ make([]int, len(kernel.data), len(kernel.data)), make([][]complex128, len(kernel.data), len(kernel.data)), } for i := 0; i < len(kernel.data); i++ { sk.origin[i] = 0 sk.data[i] = []complex128{} lastNZ := 0 for j := len(kernel.data[i]) - 1; j >= 0; j-- { if cmplx.Abs(kernel.data[i][j]) != 0 { lastNZ = j break } } haveNZ := false for j := 0; j <= lastNZ; j++ { if haveNZ || cmplx.Abs(kernel.data[i][j]) != 0 { if !haveNZ { sk.origin[i] = j } haveNZ = true sk.data[i] = append(sk.data[i], complexTimes(cmplx.Conj(kernel.data[i][j]), weight)) } } } return &CQKernel{p, &sk} }
func ExtFFT_C(samples vlib.VectorC, N int) vlib.VectorC { y := vlib.VectorC(fft.FFT(samples)) y = y.Scale(math.Sqrt(float64(N))) return y }
func (self *Server) startAudio() error { in := make([]int32, self.Config.FftSize) stream, err := self.openAudioStream(in) if err = stream.Start(); err != nil { return err } go func() { defer stream.Close() defer stream.Stop() fftSize := self.Config.FftSize halfFftSize := fftSize / 2 phaseI := make([]float64, fftSize) phaseQ := make([]float64, fftSize) complexIQ := make([]complex128, fftSize) fftResult := make([]float32, fftSize) fftCorrection := func(freq float64) float64 { return math.Pow(2.0, freq/41000) } fftBinBandWidth := stream.Info().SampleRate / float64(fftSize) for self.running { if err = stream.Read(); err != nil { log.Printf("portaudio: stream.Read() failed: %s", err) continue } if len(self.sessions) == 0 { continue } waitingClient := 0 now := time.Now().UnixNano() for _, session := range self.sessions { if !session.initialized { continue } if now-session.lastTime < session.rateLimit { continue } waitingClient++ } if waitingClient == 0 { continue } for i := 0; i < len(in); i += 2 { // left phaseI[i/2] = float64(in[i]) / 0x1000000 // right phaseQ[i/2] = float64(in[i+1]) / 0x1000000 } windowFunc := window.Hamming window.Apply(phaseI, windowFunc) window.Apply(phaseQ, windowFunc) for i := 0; i < fftSize; i++ { complexIQ[i] = complex(phaseI[i], phaseQ[i]) } result := fft.FFT(complexIQ) // real for i := 0; i < halfFftSize; i++ { power := math.Sqrt(real(result[i])*real(result[i]) + imag(result[i])*imag(result[i])) fftResult[i+halfFftSize] = float32(20 * math.Log10(power*fftCorrection(float64(i)*fftBinBandWidth))) } // imag for i := halfFftSize; i < fftSize; i++ { power := math.Sqrt(real(result[i])*real(result[i]) + imag(result[i])*imag(result[i])) fftResult[i-halfFftSize] = float32(20 * math.Log10(power*fftCorrection(float64(fftSize-i)*fftBinBandWidth))) } self.fftResult <- fftResult } }() return nil }