From 2b73b5825e4c04ef4fa6e24caa6cca0b11ccea76 Mon Sep 17 00:00:00 2001 From: Chigozirim Igweamaka Date: Wed, 15 May 2024 05:08:02 +0100 Subject: [PATCH] initial implementation of finding matches --- shazam/shazamInit.go | 135 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 shazam/shazamInit.go diff --git a/shazam/shazamInit.go b/shazam/shazamInit.go new file mode 100644 index 0000000..75655be --- /dev/null +++ b/shazam/shazamInit.go @@ -0,0 +1,135 @@ +package shazam + +import ( + "fmt" + "song-recognition/models" + "song-recognition/utils" + "sort" +) + +type Match1 struct { + SongID uint32 + SongTitle string + SongArtist string + YouTubeID string + Timestamp uint32 + Coherency float64 +} + +func Search(audioSamples []float64, audioDuration float64, sampleRate int) ([]Match1, error) { + spectrogram, err := Spectrogram(audioSamples, sampleRate) + if err != nil { + return nil, fmt.Errorf("failed to get spectrogram of samples: %v", err) + } + + peaks := ExtractPeaks(spectrogram, audioDuration) + fingerprints := Fingerprint(peaks, utils.GenerateUniqueID()) + + addresses := make([]uint32, 0, len(fingerprints)) + for address, _ := range fingerprints { + addresses = append(addresses, address) + } + + db, err := utils.NewDbClient() + if err != nil { + return nil, err + } + defer db.Close() + + couples, err := db.GetCouples(addresses) + if err != nil { + return nil, err + } + + targetZones := targetZones(couples) + fmt.Println("TargetZones: ", targetZones) + matches := timeCoherency(fingerprints, targetZones) + + var matchList []Match1 + for songID, coherency := range matches { + song, songExists, err := db.GetSongByID(songID) + if err != nil || !songExists { + return nil, err + } + + timestamp := targetZones[songID][0] + match := Match1{songID, song.Title, song.Artist, song.YouTubeID, timestamp, float64(coherency)} + + matchList = append(matchList, match) + } + + sort.Slice(matchList, func(i, j int) bool { + return matchList[i].Coherency > matchList[j].Coherency + }) + + return matchList, nil +} + +func targetZones(m map[uint32][]models.Couple) map[uint32][]uint32 { + songs := make(map[uint32]map[uint32]int) + + for _, couples := range m { + for _, couple := range couples { + if _, ok := songs[couple.SongID]; !ok { + songs[couple.SongID] = make(map[uint32]int) + } + songs[couple.SongID][couple.AnchorTimeMs]++ + } + } + fmt.Println("couples: ", songs) + + for songID, anchorTimes := range songs { + for msTime, count := range anchorTimes { + if count < 5 { + delete(songs[songID], msTime) + } + } + } + fmt.Println("anchorTimes: ", songs) + + targetZones := make(map[uint32][]uint32) + for songID, anchorTimes := range songs { + for anchorTime, _ := range anchorTimes { + targetZones[songID] = append(targetZones[songID], anchorTime) + } + } + + return targetZones +} + +func timeCoherency(record map[uint32]models.Couple, songs map[uint32][]uint32) map[uint32]int { + // var threshold float64 + matches := make(map[uint32]int) + + for songID, songAnchorTimes := range songs { + deltas := make(map[float64]int) + for _, songAnchorTime := range songAnchorTimes { + for _, recordAnchor := range record { + recordAnchorTimeMs := float64(recordAnchor.AnchorTimeMs) + delta := recordAnchorTimeMs - float64(songAnchorTime) + deltas[delta]++ + } + } + + // Find the maximum number of time-coherent notes + var maxOccurrences int + for _, occurrences := range deltas { + if occurrences > maxOccurrences { + maxOccurrences = occurrences + } + } + + matches[songID] = maxOccurrences + } + + // Apply threshold for coherency + /** + for songID, coherency := range matches { + if float64(coherency) < threshold*float64(len(record)) { + delete(matches, songID) // Remove songs with insufficient coherency + } + } + */ + + return matches +}