diff --git a/shazam/shazam.go b/shazam/shazam.go index de9d5fd..52a90cc 100644 --- a/shazam/shazam.go +++ b/shazam/shazam.go @@ -3,7 +3,6 @@ package shazam import ( "fmt" "math" - "song-recognition/models" "song-recognition/utils" "sort" ) @@ -17,6 +16,7 @@ type Match struct { Score float64 } +// FindMatches processes the audio samples and finds matches in the database func FindMatches(audioSamples []float64, audioDuration float64, sampleRate int) ([]Match, error) { logger := utils.GetLogger() @@ -27,10 +27,9 @@ func FindMatches(audioSamples []float64, audioDuration float64, sampleRate int) peaks := ExtractPeaks(spectrogram, audioDuration) fingerprints := Fingerprint(peaks, utils.GenerateUniqueID()) - fmt.Println("peaks len: ", len(peaks)) addresses := make([]uint32, 0, len(fingerprints)) - for address, _ := range fingerprints { + for address := range fingerprints { addresses = append(addresses, address) } @@ -45,34 +44,17 @@ func FindMatches(audioSamples []float64, audioDuration float64, sampleRate int) return nil, err } - matches := map[uint32]map[uint32]models.Couple{} + matches := map[uint32][][2]uint32{} // songID -> [(sampleTime, dbTime)] timestamps := map[uint32]uint32{} for address, couples := range m { for _, couple := range couples { - - if _, ok := matches[couple.SongID]; !ok { - matches[couple.SongID] = map[uint32]models.Couple{} - timestamps[couple.SongID] = couple.AnchorTimeMs - } - - matches[couple.SongID][address] = couple + matches[couple.SongID] = append(matches[couple.SongID], [2]uint32{fingerprints[address].AnchorTimeMs, couple.AnchorTimeMs}) + timestamps[couple.SongID] = couple.AnchorTimeMs } } - scores := map[uint32]float64{} - for songID, couples := range matches { - song, songExists, err := db.GetSongByID(songID) - if err != nil || !songExists { - // log error - fmt.Println("Continuing") - continue - } - fmt.Printf("Song: %v, Scores:\n", song.Title) - - scores[songID] = matchScore(fingerprints, couples) - fmt.Println("------------------------------------") - } + scores := analyzeRelativeTiming(matches) var matchList []Match for songID, points := range scores { @@ -87,7 +69,6 @@ func FindMatches(audioSamples []float64, audioDuration float64, sampleRate int) } fmt.Printf("Song: %v, Score: %v\n", song.Title, points) - fmt.Println("====================================") match := Match{songID, song.Title, song.Artist, song.YouTubeID, timestamps[songID], points} matchList = append(matchList, match) } @@ -100,60 +81,21 @@ func FindMatches(audioSamples []float64, audioDuration float64, sampleRate int) return matchList, nil } -// MatchScore computes a match score between the two transformed audio samples (into a list of Key + TableValue) -func matchScore(sample, match map[uint32]models.Couple) float64 { - // Will hold a list of points (time in the sample sound file, time in the matched database sound file) - points := [2][]float64{} - matches := 0.0 - for k, sampleValue := range sample { - if matchValue, ok := match[k]; ok { - points[0] = append(points[0], float64(sampleValue.AnchorTimeMs)) - points[1] = append(points[1], float64(matchValue.AnchorTimeMs)) - matches++ +// AnalyzeRelativeTiming checks for consistent relative timing and returns a score +func analyzeRelativeTiming(matches map[uint32][][2]uint32) map[uint32]float64 { + scores := make(map[uint32]float64) + for songID, times := range matches { + count := 0 + for i := 0; i < len(times); i++ { + for j := i + 1; j < len(times); j++ { + sampleDiff := math.Abs(float64(times[i][0] - times[j][0])) + dbDiff := math.Abs(float64(times[i][1] - times[j][1])) + if math.Abs(sampleDiff-dbDiff) < 100 { // Allow some tolerance + count++ + } + } } + scores[songID] = float64(count) } - corr := correlation(points[0], points[1]) - fmt.Printf("Score (%v * %v * %v): %v\n", corr, corr, matches, corr*corr*matches) - return corr * corr * matches -} - -// Correlation computes the correlation between 2 series of points -// the length used is x's -func correlation(x []float64, y []float64) float64 { - n := len(x) - meanX, meanY := Avg(x[:n]), Avg(y[:n]) - - sXY := 0.0 - sX := 0.0 - sY := 0.0 - - for i, xp := range x { - dx := xp - meanX - dy := y[i] - meanY - - sX += dx * dx - sY += dy * dy - - sXY += dx * dy - } - - if sX == 0 || sY == 0 { - return 0 - } - - return sXY / (math.Sqrt(sX) * math.Sqrt(sY)) -} - -// Avg computes the average of the given array -func Avg(arr []float64) float64 { - if len(arr) == 0 { - return 0 - } - - sum := 0.0 - for _, v := range arr { - sum += v - } - - return sum / float64(len(arr)) + return scores }