mirror of
https://github.com/cgzirim/seek-tune.git
synced 2025-12-17 17:04:22 +00:00
refactor: update FindMatches function
This commit is contained in:
parent
177c654cc8
commit
495163ebb4
2 changed files with 43 additions and 4 deletions
|
|
@ -1,3 +1,6 @@
|
||||||
|
//go:build !js && !wasm
|
||||||
|
// +build !js,!wasm
|
||||||
|
|
||||||
package shazam
|
package shazam
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -21,7 +24,6 @@ type Match struct {
|
||||||
// FindMatches analyzes the audio sample to find matching songs in the database.
|
// FindMatches analyzes the audio sample to find matching songs in the database.
|
||||||
func FindMatches(audioSample []float64, audioDuration float64, sampleRate int) ([]Match, time.Duration, error) {
|
func FindMatches(audioSample []float64, audioDuration float64, sampleRate int) ([]Match, time.Duration, error) {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
logger := utils.GetLogger()
|
|
||||||
|
|
||||||
spectrogram, err := Spectrogram(audioSample, sampleRate)
|
spectrogram, err := Spectrogram(audioSample, sampleRate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -31,6 +33,21 @@ func FindMatches(audioSample []float64, audioDuration float64, sampleRate int) (
|
||||||
peaks := ExtractPeaks(spectrogram, audioDuration)
|
peaks := ExtractPeaks(spectrogram, audioDuration)
|
||||||
sampleFingerprint := Fingerprint(peaks, utils.GenerateUniqueID())
|
sampleFingerprint := Fingerprint(peaks, utils.GenerateUniqueID())
|
||||||
|
|
||||||
|
sampleFingerprintMap := make(map[uint32]uint32)
|
||||||
|
for address, couple := range sampleFingerprint {
|
||||||
|
sampleFingerprintMap[address] = couple.AnchorTimeMs
|
||||||
|
}
|
||||||
|
|
||||||
|
matches, _, err := FindMatchesFGP(sampleFingerprintMap)
|
||||||
|
|
||||||
|
return matches, time.Since(startTime), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindMatchesFGP uses the sample fingerprint to find matching songs in the database.
|
||||||
|
func FindMatchesFGP(sampleFingerprint map[uint32]uint32) ([]Match, time.Duration, error) {
|
||||||
|
startTime := time.Now()
|
||||||
|
logger := utils.GetLogger()
|
||||||
|
|
||||||
addresses := make([]uint32, 0, len(sampleFingerprint))
|
addresses := make([]uint32, 0, len(sampleFingerprint))
|
||||||
for address := range sampleFingerprint {
|
for address := range sampleFingerprint {
|
||||||
addresses = append(addresses, address)
|
addresses = append(addresses, address)
|
||||||
|
|
@ -55,7 +72,7 @@ func FindMatches(audioSample []float64, audioDuration float64, sampleRate int) (
|
||||||
for _, couple := range couples {
|
for _, couple := range couples {
|
||||||
matches[couple.SongID] = append(
|
matches[couple.SongID] = append(
|
||||||
matches[couple.SongID],
|
matches[couple.SongID],
|
||||||
[2]uint32{sampleFingerprint[address].AnchorTimeMs, couple.AnchorTimeMs},
|
[2]uint32{sampleFingerprint[address], couple.AnchorTimeMs},
|
||||||
)
|
)
|
||||||
|
|
||||||
if existingTime, ok := timestamps[couple.SongID]; !ok || couple.AnchorTimeMs < existingTime {
|
if existingTime, ok := timestamps[couple.SongID]; !ok || couple.AnchorTimeMs < existingTime {
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func Spectrogram(sample []float64, sampleRate int) ([][]complex128, error) {
|
func Spectrogram(sample []float64, sampleRate int) ([][]complex128, error) {
|
||||||
lpf := NewLowPassFilter(maxFreq, float64(sampleRate))
|
filteredSample := LowPassFilter(maxFreq, float64(sampleRate), sample)
|
||||||
filteredSample := lpf.Filter(sample)
|
|
||||||
|
|
||||||
downsampledSample, err := Downsample(filteredSample, sampleRate, sampleRate/dspRatio)
|
downsampledSample, err := Downsample(filteredSample, sampleRate, sampleRate/dspRatio)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -53,6 +52,29 @@ func Spectrogram(sample []float64, sampleRate int) ([][]complex128, error) {
|
||||||
return spectrogram, nil
|
return spectrogram, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LowPassFilter is a first-order low-pass filter that attenuates high
|
||||||
|
// frequencies above the cutoffFrequency.
|
||||||
|
// It uses the transfer function H(s) = 1 / (1 + sRC), where RC is the time constant.
|
||||||
|
func LowPassFilter(cutoffFrequency, sampleRate float64, input []float64) []float64 {
|
||||||
|
rc := 1.0 / (2 * math.Pi * cutoffFrequency)
|
||||||
|
dt := 1.0 / sampleRate
|
||||||
|
alpha := dt / (rc + dt)
|
||||||
|
|
||||||
|
filteredSignal := make([]float64, len(input))
|
||||||
|
var prevOutput float64 = 0
|
||||||
|
|
||||||
|
for i, x := range input {
|
||||||
|
if i == 0 {
|
||||||
|
filteredSignal[i] = x * alpha
|
||||||
|
} else {
|
||||||
|
|
||||||
|
filteredSignal[i] = alpha*x + (1-alpha)*prevOutput
|
||||||
|
}
|
||||||
|
prevOutput = filteredSignal[i]
|
||||||
|
}
|
||||||
|
return filteredSignal
|
||||||
|
}
|
||||||
|
|
||||||
// Downsample downsamples the input audio from originalSampleRate to targetSampleRate
|
// Downsample downsamples the input audio from originalSampleRate to targetSampleRate
|
||||||
func Downsample(input []float64, originalSampleRate, targetSampleRate int) ([]float64, error) {
|
func Downsample(input []float64, originalSampleRate, targetSampleRate int) ([]float64, error) {
|
||||||
if targetSampleRate <= 0 || originalSampleRate <= 0 {
|
if targetSampleRate <= 0 || originalSampleRate <= 0 {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue