// Pwelch estimates the power spectral density of x using Welch's method. // Fs is the sampling frequency (samples per time unit) of x. Fs is used // to calculate freqs. // Returns the power spectral density Pxx and corresponding frequencies freqs. // Designed to be similar to the matplotlib implementation below. // Reference: http://matplotlib.org/api/mlab_api.html#matplotlib.mlab.psd // See also: http://www.mathworks.com/help/signal/ref/pwelch.html func Pwelch(x []float64, Fs float64, o *PwelchOptions) (Pxx, freqs []float64) { if len(x) == 0 { return []float64{}, []float64{} } nfft := o.NFFT pad := o.Pad noverlap := o.Noverlap wf := o.Window enable_scaling := !o.Scale_off if nfft == 0 { nfft = 256 } if wf == nil { wf = window.Hann } if pad == 0 { pad = nfft } if len(x) < nfft { x = dsputils.ZeroPadF(x, nfft) } lp := pad/2 + 1 var scale float64 = 2 segs := Segment(x, nfft, noverlap) Pxx = make([]float64, lp) for _, x := range segs { x = dsputils.ZeroPadF(x, pad) window.Apply(x, wf) pgram := fft.FFTReal(x) for j := range Pxx { d := real(cmplx.Conj(pgram[j])*pgram[j]) / float64(len(segs)) if j > 0 && j < lp-1 { d *= scale } Pxx[j] += d } } w := wf(nfft) var norm float64 for _, x := range w { norm += math.Pow(x, 2) } if enable_scaling { norm *= Fs } for i := range Pxx { Pxx[i] /= norm } freqs = make([]float64, lp) coef := Fs / float64(pad) for i := range freqs { freqs[i] = float64(i) * coef } return }
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 }