refactor: update FindMatches function

This commit is contained in:
Chigozirim Igweamaka 2025-03-11 09:18:29 +01:00
parent 177c654cc8
commit 495163ebb4
2 changed files with 43 additions and 4 deletions

View file

@ -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 {

View file

@ -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 {