From 495163ebb43fa8b8cea868c2be3df18aa64eb5df Mon Sep 17 00:00:00 2001 From: Chigozirim Igweamaka Date: Tue, 11 Mar 2025 09:18:29 +0100 Subject: [PATCH] refactor: update FindMatches function --- shazam/shazam.go | 21 +++++++++++++++++++-- shazam/spectrogram.go | 26 ++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/shazam/shazam.go b/shazam/shazam.go index 503b605..0683019 100644 --- a/shazam/shazam.go +++ b/shazam/shazam.go @@ -1,3 +1,6 @@ +//go:build !js && !wasm +// +build !js,!wasm + package shazam import ( @@ -21,7 +24,6 @@ type Match struct { // FindMatches analyzes the audio sample to find matching songs in the database. func FindMatches(audioSample []float64, audioDuration float64, sampleRate int) ([]Match, time.Duration, error) { startTime := time.Now() - logger := utils.GetLogger() spectrogram, err := Spectrogram(audioSample, sampleRate) if err != nil { @@ -31,6 +33,21 @@ func FindMatches(audioSample []float64, audioDuration float64, sampleRate int) ( peaks := ExtractPeaks(spectrogram, audioDuration) 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)) for address := range sampleFingerprint { addresses = append(addresses, address) @@ -55,7 +72,7 @@ func FindMatches(audioSample []float64, audioDuration float64, sampleRate int) ( for _, couple := range couples { matches[couple.SongID] = append( 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 { diff --git a/shazam/spectrogram.go b/shazam/spectrogram.go index e18ae53..2488019 100644 --- a/shazam/spectrogram.go +++ b/shazam/spectrogram.go @@ -15,8 +15,7 @@ const ( ) func Spectrogram(sample []float64, sampleRate int) ([][]complex128, error) { - lpf := NewLowPassFilter(maxFreq, float64(sampleRate)) - filteredSample := lpf.Filter(sample) + filteredSample := LowPassFilter(maxFreq, float64(sampleRate), sample) downsampledSample, err := Downsample(filteredSample, sampleRate, sampleRate/dspRatio) if err != nil { @@ -53,6 +52,29 @@ func Spectrogram(sample []float64, sampleRate int) ([][]complex128, error) { 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 func Downsample(input []float64, originalSampleRate, targetSampleRate int) ([]float64, error) { if targetSampleRate <= 0 || originalSampleRate <= 0 {