66 lines
1.7 KiB
Go
66 lines
1.7 KiB
Go
package extractfreq
|
|
|
|
import (
|
|
"log"
|
|
|
|
"gonum.org/v1/gonum/floats"
|
|
"gonum.org/v1/gonum/stat"
|
|
)
|
|
|
|
type HarmonicWindow struct {
|
|
Harmonic float64 // 0.25, 0.5, 1, 2, 3, ...
|
|
Amps []float64
|
|
Freqs []float64
|
|
IdxPeak int
|
|
IdxPeakInterp float64
|
|
FreqPeakInterp float64
|
|
AmpPeak float64
|
|
AmpMean float64
|
|
AmpStd float64
|
|
}
|
|
|
|
func NewHarmonicWindow(
|
|
fFundamental float64,
|
|
harmonic float64,
|
|
rawAmps []float64,
|
|
rawFreqs []float64,
|
|
) *HarmonicWindow {
|
|
deltaF := rawFreqs[1] - rawFreqs[0]
|
|
|
|
// TODO: At higher harmonic numbers, the harmonics are going to be spaced
|
|
// more cloesly together. That means that the window size must shrink.
|
|
//winSizeCents := float64(150)
|
|
|
|
// To start, we create a window around the approximate harmonic location.
|
|
idxPeak := int((fFundamental * harmonic) / deltaF)
|
|
winSize := int(0.5 * fFundamental / deltaF)
|
|
amps := rawAmps[idxPeak-winSize : idxPeak+winSize]
|
|
freqs := rawFreqs[idxPeak-winSize : idxPeak+winSize]
|
|
|
|
// Next, we refine the window be centering it on the actual peak.
|
|
idxPeak += (floats.MaxIdx(amps) - winSize)
|
|
amps = rawAmps[idxPeak-winSize : idxPeak+winSize]
|
|
freqs = rawFreqs[idxPeak-winSize : idxPeak+winSize]
|
|
idxPeak = winSize
|
|
|
|
// Next we compute statistical properties to determine if this peak is worth
|
|
// using.
|
|
mean, std := stat.MeanStdDev(amps, nil)
|
|
ampPeak := amps[idxPeak]
|
|
|
|
log.Printf("%0.2f: %0.4f", harmonic, (ampPeak-mean)/std)
|
|
// TODO: Actual interpolation.
|
|
|
|
return &HarmonicWindow{
|
|
Harmonic: harmonic,
|
|
Amps: amps,
|
|
Freqs: freqs,
|
|
IdxPeak: idxPeak,
|
|
IdxPeakInterp: float64(idxPeak),
|
|
FreqPeakInterp: freqs[idxPeak],
|
|
AmpPeak: ampPeak,
|
|
AmpMean: mean,
|
|
AmpStd: std,
|
|
}
|
|
}
|