WIP: peak finding
parent
90fbc089f4
commit
72af0bdd82
|
@ -2,8 +2,11 @@ package extractfreq
|
||||||
|
|
||||||
import "math"
|
import "math"
|
||||||
|
|
||||||
|
// A-weight filter is supposed to correspond to how a human would perceive
|
||||||
|
// loudness.
|
||||||
func AWeight(f float64) float64 {
|
func AWeight(f float64) float64 {
|
||||||
return (148693636.0 * f * f * f * f) / ((424.36 + f*f) * math.Sqrt(
|
return (148693636.0 * f * f * f * f) /
|
||||||
(f*f+11599.29)*(f*f+544496.41)) *
|
((424.36 + f*f) *
|
||||||
|
math.Sqrt((f*f+11599.29)*(f*f+544496.41)) *
|
||||||
(f*f + 148693636.0))
|
(f*f + 148693636.0))
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,12 @@ import (
|
||||||
|
|
||||||
// Steps:
|
// Steps:
|
||||||
//
|
//
|
||||||
// * Create amplitude spectrum (abs of FFT).
|
// * Create amplitude spectrum (abs of real FFT).
|
||||||
// * Multiply by A-weight curve
|
// * Multiply by A-weight curve
|
||||||
// * Take log10 of result
|
// * Find peaks
|
||||||
|
// * Sort peaks by amplitude
|
||||||
|
// * Use strongest peak to estimate fFundamental?
|
||||||
|
// *
|
||||||
func PlotFFT(path string, nSamples int, fFundamental float64) {
|
func PlotFFT(path string, nSamples int, fFundamental float64) {
|
||||||
L, R, err := flac.Load(path)
|
L, R, err := flac.Load(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -34,98 +37,96 @@ func PlotFFT(path string, nSamples int, fFundamental float64) {
|
||||||
data[i] = float64(L[i]) + float64(R[i])
|
data[i] = float64(L[i]) + float64(R[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make initial fft.
|
||||||
fft := fourier.NewFFT(nSamples)
|
fft := fourier.NewFFT(nSamples)
|
||||||
cCoeffs := fft.Coefficients(nil, data)
|
cCoeffs := fft.Coefficients(nil, data)
|
||||||
amps := make([]float64, len(cCoeffs))
|
amps := make([]float64, len(cCoeffs))
|
||||||
freqs := make([]float64, len(cCoeffs))
|
freqs := make([]float64, len(cCoeffs))
|
||||||
for i := range freqs {
|
for i := range freqs {
|
||||||
freqs[i] = fft.Freq(i) * 48000
|
freqs[i] = fft.Freq(i) * 48000
|
||||||
if freqs[i] < 20 {
|
amps[i] = cmplx.Abs(cCoeffs[i])
|
||||||
amps[i] = 0
|
|
||||||
} else {
|
|
||||||
amps[i] = math.Log10(cmplx.Abs(cCoeffs[i]) * AWeight(freqs[i]))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize coefficients.
|
// Trim to frequencies of interest.
|
||||||
//for i := range amps {
|
deltaF := freqs[1] - freqs[0]
|
||||||
//amps[i] /= ampMax
|
idx0 := int(20 / deltaF)
|
||||||
//}
|
idx1 := int(16000 / deltaF)
|
||||||
|
freqs = freqs[idx0:idx1]
|
||||||
|
amps = amps[idx0:idx1]
|
||||||
|
|
||||||
// Find highest N peaks and sort by size.
|
// Multiply by A-weight function.
|
||||||
|
for i := range amps {
|
||||||
/*
|
amps[i] *= AWeight(freqs[i])
|
||||||
// Use a moving window to search for local peaks.
|
|
||||||
hws := []*HarmonicWindow{}
|
|
||||||
|
|
||||||
// Bypass low-frequency samples.
|
|
||||||
i := 0
|
|
||||||
for i < len(freqs) && freqs[i] < 24 {
|
|
||||||
i++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
iCenter := i
|
// Find peaks.
|
||||||
for iC{
|
dFreq := freqs[1] - freqs[0]
|
||||||
|
halfWinSize := int(math.Round((fFundamental / dFreq) / 3))
|
||||||
}
|
peakInds, peakAmps := getPeaks(halfWinSize, amps)
|
||||||
*/
|
peakInds, peakAmps = sortPeaks(peakInds, peakAmps)
|
||||||
// Find frequency window around midi note.
|
|
||||||
|
|
||||||
p, err := plot.New()
|
p, err := plot.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fMin := float64(20)
|
||||||
|
fMax := float64(16000)
|
||||||
|
|
||||||
log.Printf("Making points...")
|
log.Printf("Making points...")
|
||||||
pts := make(plotter.XYs, 0, len(amps))
|
pts := make(plotter.XYs, 0, len(amps))
|
||||||
for i := range amps {
|
for i := range amps {
|
||||||
if freqs[i] < 20 || freqs[i] > 16000 {
|
if freqs[i] > fMin && freqs[i] < fMax {
|
||||||
continue
|
|
||||||
}
|
|
||||||
pts = append(pts, plotter.XY{
|
pts = append(pts, plotter.XY{
|
||||||
X: freqs[i],
|
X: freqs[i],
|
||||||
Y: amps[i],
|
Y: amps[i],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Plotting...")
|
|
||||||
if err = plotutil.AddLinePoints(p, "fft", pts); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//p.Y.Scale = plot.LogScale{}
|
pts2 := make(plotter.XYs, 0, len(amps))
|
||||||
p.X.Scale = plot.LogScale{}
|
for i := range peakInds {
|
||||||
|
if freqs[peakInds[i]] > fMin && freqs[peakInds[i]] < fMax {
|
||||||
|
pts2 = append(pts2, plotter.XY{
|
||||||
|
X: freqs[peakInds[i]],
|
||||||
|
Y: peakAmps[i],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Plotting...")
|
||||||
|
if err = plotutil.AddScatters(p, "fft", pts, "max", pts2); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
p.Add(plotter.NewGrid())
|
||||||
|
|
||||||
log.Printf("Saving...")
|
log.Printf("Saving...")
|
||||||
if err := p.Save(16*vg.Inch, 8*vg.Inch, "fft.png"); err != nil {
|
if err := p.Save(16*vg.Inch, 8*vg.Inch, "fft.png"); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
harmonics := []float64{0.5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
for i, idx := range peakInds {
|
||||||
hws := []*HarmonicWindow{}
|
//amp := peakAmps[i]
|
||||||
for _, harmonic := range harmonics {
|
|
||||||
|
|
||||||
hw := NewHarmonicWindow(
|
// Make window.
|
||||||
fFundamental,
|
ampsWin := amps[idx-halfWinSize : idx+halfWinSize]
|
||||||
harmonic,
|
freqWin := freqs[idx-halfWinSize : idx+halfWinSize]
|
||||||
amps,
|
|
||||||
freqs)
|
|
||||||
|
|
||||||
|
log.Printf("Making points...")
|
||||||
|
pts := make(plotter.XYs, 0, 2*halfWinSize)
|
||||||
|
for i := range ampsWin {
|
||||||
|
pts = append(pts, plotter.XY{
|
||||||
|
X: freqWin[i],
|
||||||
|
Y: ampsWin[i],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Plotting...")
|
||||||
p, err := plot.New()
|
p, err := plot.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Making points...")
|
|
||||||
pts := make(plotter.XYs, 0, len(hw.Amps))
|
|
||||||
for i := range hw.Amps {
|
|
||||||
pts = append(pts, plotter.XY{
|
|
||||||
X: hw.Freqs[i],
|
|
||||||
Y: hw.Amps[i],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Plotting...")
|
|
||||||
if err = plotutil.AddLines(p, "fft", pts); err != nil {
|
if err = plotutil.AddLines(p, "fft", pts); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -134,31 +135,9 @@ func PlotFFT(path string, nSamples int, fFundamental float64) {
|
||||||
//p.X.Scale = plot.LogScale{}
|
//p.X.Scale = plot.LogScale{}
|
||||||
|
|
||||||
log.Printf("Saving...")
|
log.Printf("Saving...")
|
||||||
filename := fmt.Sprintf("plot-%03.2f.png", harmonic)
|
filename := fmt.Sprintf("plot-%03d.png", i)
|
||||||
if err := p.Save(8*vg.Inch, 8*vg.Inch, filename); err != nil {
|
if err := p.Save(8*vg.Inch, 8*vg.Inch, filename); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
hws = append(hws, hw)
|
|
||||||
}
|
|
||||||
|
|
||||||
pts = make(plotter.XYs, len(hws))
|
|
||||||
for i, hw := range hws[:11] {
|
|
||||||
pts[i].X = hw.Harmonic
|
|
||||||
pts[i].Y = hw.FreqPeakInterp
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err = plot.New()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = plotutil.AddScatters(p, "", pts); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Saving...")
|
|
||||||
if err := p.Save(8*vg.Inch, 8*vg.Inch, "harmonics.png"); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue