Beispiel #1
0
        private void CalculateSubFingerprint(float[] energyBands, float[] previousEnergyBands, List <SubFingerprint> list)
        {
            SubFingerprintHash      hash            = new SubFingerprintHash();
            Dictionary <int, float> bitReliability  = new Dictionary <int, float>();
            List <SubFingerprint>   subFingerprints = new List <SubFingerprint>(1 + flipWeakestBits);

            for (int m = 0; m < 32; m++)
            {
                float difference = energyBands[m] - energyBands[m + 1] - (previousEnergyBands[m] - previousEnergyBands[m + 1]);
                hash[m] = difference > 0;
                bitReliability.Add(m, difference > 0 ? difference : -difference); // take absolute value as reliability weight
            }

            list.Add(new SubFingerprint(index, hash, false));

            if (flipWeakestBits > 0)
            {
                // calculate probable hashes by flipping the most unreliable bits (the bits with the least energy differences)
                List <int> weakestBits = new List <int>(bitReliability.Keys.OrderBy(key => bitReliability[key]));
                // generate hashes with all possible bit combinations flipped
                int variations = 1 << flipWeakestBits;
                for (int i = 1; i < variations; i++)   // start at 1 since i0 equals to the original hash
                {
                    SubFingerprintHash flippedHash = new SubFingerprintHash(hash.Value);
                    for (int j = 0; j < flipWeakestBits; j++)
                    {
                        if (((i >> j) & 1) == 1)
                        {
                            flippedHash[weakestBits[j]] = !flippedHash[weakestBits[j]];
                        }
                    }
                    list.Add(new SubFingerprint(index, flippedHash, true));
                }
            }
        }
Beispiel #2
0
 private void btnFindMatches_Click(object sender, RoutedEventArgs e)
 {
     if (trackFingerprintListBox.SelectedItems.Count > 0)
     {
         SubFingerprintHash hash = (SubFingerprintHash)trackFingerprintListBox.SelectedItem;
         var matches             = store.FindMatches(hash);
         ListMatches(matches);
     }
 }
Beispiel #3
0
 private void trackFingerprintListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
 {
     fingerprintMatchListBox.Items.Clear();
     if (trackFingerprintListBox.SelectedItems.Count > 0)
     {
         SubFingerprintHash hash = (SubFingerprintHash)trackFingerprintListBox.SelectedItem;
         foreach (SubFingerprintLookupEntry lookupEntry in store.CollisionMap.GetValues(hash))
         {
             fingerprintMatchListBox.Items.Add(lookupEntry);
         }
     }
 }
Beispiel #4
0
        public List <Match> FindMatches(SubFingerprintHash hash)
        {
            List <Match> matches = new List <Match>();
            List <SubFingerprintLookupEntry> entries = collisionMap.GetValues(hash);

            //Debug.WriteLine("Finding matches...");

            // compare each track with each other
            int cycle = 1;

            for (int x = 0; x < entries.Count; x++)
            {
                SubFingerprintLookupEntry entry1 = entries[x];
                for (int y = cycle; y < entries.Count; y++)
                {
                    SubFingerprintLookupEntry entry2 = entries[y];
                    if (entry1.AudioTrack != entry2.AudioTrack)   // don't compare tracks with themselves
                    //Debug.WriteLine("Comparing " + entry1.AudioTrack.Name + " with " + entry2.AudioTrack.Name + ":");
                    {
                        if (store[entry1.AudioTrack].Count - entry1.Index < fingerprintSize ||
                            store[entry2.AudioTrack].Count - entry2.Index < fingerprintSize)
                        {
                            // the end of at least one track has been reached and there are not enough hashes left
                            // to do a fingerprint comparison
                            continue;
                        }

                        // sum up the bit errors
                        List <SubFingerprintHash> track1Hashes = store[entry1.AudioTrack];
                        List <SubFingerprintHash> track2Hashes = store[entry2.AudioTrack];
                        uint bitErrors = 0;
                        for (int s = 0; s < fingerprintSize; s++)
                        {
                            SubFingerprintHash track1Hash = track1Hashes[entry1.Index + s];
                            SubFingerprintHash track2Hash = track2Hashes[entry2.Index + s];
                            if (track1Hash.Value == 0 || track2Hash.Value == 0)
                            {
                                bitErrors = (uint)fingerprintSize * 32;
                                break;
                            }
                            // skip fingerprints with hashes that are zero, since it is probably from
                            // a track section with silence
                            // by setting the bitErrors to the maximum, the match will not be added
                            bitErrors += track1Hash.HammingDistance(track2Hash);
                        }

                        float bitErrorRate = bitErrors / (float)(fingerprintSize * 32); // sub-fingerprints * 32 bits
                        //Debug.WriteLine("BER: " + bitErrorRate + " <- " + (bitErrorRate < threshold ? "MATCH!!!" : "no match"));
                        if (bitErrorRate < threshold)
                        {
                            matches.Add(new Match {
                                Similarity = 1 - bitErrorRate,
                                Track1     = entry1.AudioTrack,
                                Track1Time = SubFingerprintIndexToTimeSpan(entry1.Index),
                                Track2     = entry2.AudioTrack,
                                Track2Time = SubFingerprintIndexToTimeSpan(entry2.Index),
                                Source     = matchSourceName
                            });
                        }
                    }
                }
                cycle++;
            }

            //Debug.WriteLine("finished");
            return(matches);
        }
        public List <Match> FindMatches(SubFingerprintHash hash)
        {
            List <Match> matches = new List <Match>();
            List <SubFingerprintLookupEntry> entries = collisionMap.GetValues(hash);

            for (int x = 0; x < entries.Count; x++)
            {
                SubFingerprintLookupEntry entry1 = entries[x];
                for (int y = x; y < entries.Count; y++)
                {
                    SubFingerprintLookupEntry entry2 = entries[y];
                    if (entry1.AudioTrack != entry2.AudioTrack)   // don't compare tracks with themselves

                    {
                        var store1 = store[entry1.AudioTrack];
                        var store2 = store[entry2.AudioTrack];
                        List <SubFingerprintHash> hashes1 = store1.hashes;
                        List <SubFingerprintHash> hashes2 = store2.hashes;
                        int index1 = entry1.Index;
                        int index2 = entry2.Index;
                        TrackStore.IndexEntry indexEntry1, indexEntry2;
                        int  numTried   = 0; // count of hashes tried to match
                        int  numMatched = 0; // count of hashes matched
                        int  frameCount = 0; // count over how many actual frames hashes were matched (an index in the store is the equivalent of a frame in the generator)
                        bool matchFound = false;

                        // Iterate through sequential frames
                        TrackStore.IndexEntry indexEntryNone = new TrackStore.IndexEntry();
                        while (true)
                        {
                            indexEntry1 = store1.index.ContainsKey(index1) ? store1.index[index1] : indexEntryNone;
                            indexEntry2 = store2.index.ContainsKey(index2) ? store2.index[index2] : indexEntryNone;

                            // Hash collision
                            // The union of the two ranges is the total number of distinct hashes
                            // The intersection of the two ranges is the total number of similar hashes
                            // NOTE The following block calculates the number of matches (the intersection)
                            //      of the two hash lists with the Zipper intersection algorithm and relies
                            //      on the hash list sorting in the fingerprint generator.
                            //      Other approaches tried which are slower:
                            //      - n*m element by element comparison (seven though the amount of elements is reasonably small)
                            //      - concatenating the two ranges (LINQ), sorting them, and linearly iterating over, counting the duplicates (sort is slow)
                            //      - using a hash set for collision detection (hash set insertion and lookup are costly)

                            int i   = indexEntry1.index;
                            int i_e = indexEntry1.index + indexEntry1.length;
                            int j   = indexEntry2.index;
                            int j_e = indexEntry2.index + indexEntry2.length;
                            int intersectionCount = 0;

                            // Count intersecting hashes of a frame with the Zipper algorithm
                            while (i < i_e && j < j_e)
                            {
                                if (hashes1[i] < hashes2[j])
                                {
                                    i++;
                                }
                                else if (hashes2[j] < hashes1[i])
                                {
                                    j++;
                                }
                                else
                                {
                                    intersectionCount++;
                                    i++; j++;
                                }
                            }

                            numMatched += intersectionCount;
                            numTried   += indexEntry1.length + indexEntry2.length - intersectionCount;

                            // Determine the next indices to check for collisions
                            int nextIndex1Increment = 0;
                            if (hashes1.Count > i_e)
                            {
                                do
                                {
                                    nextIndex1Increment++;
                                } while (!store1.index.ContainsKey(index1 + nextIndex1Increment));
                            }
                            int nextIndex2Increment = 0;
                            if (hashes2.Count > j_e)
                            {
                                do
                                {
                                    nextIndex2Increment++;
                                } while (!store2.index.ContainsKey(index2 + nextIndex2Increment));
                            }
                            int nextIndexIncrement = Math.Min(nextIndex1Increment, nextIndex2Increment);

                            index1     += nextIndexIncrement;
                            index2     += nextIndexIncrement;
                            frameCount += nextIndexIncrement;

                            // Match detection
                            // This approach trades the hash matching rate with time, i.e. the rate required
                            // for a match drops with time, by using an exponentially with time decaying threshold.
                            // The idea is that a high matching rate after a short time is equivalent to a low matching
                            // rate after a long time. The difficulty is to to parameterize it in such a way, that a
                            // match is detected as fast as possible, while detecting a no-match isn't delayed too far
                            // as it takes a lot of processing time.
                            // NOTE The current parameters are just eyeballed, there's a lot of influence on processing speed and matching rate here
                            double rate = 1d / numTried * numMatched;

                            if (frameCount >= matchingMaxFrames || rate < thresholdReject[frameCount])
                            {
                                break; // exit condition
                            }
                            else if (frameCount > matchingMinFrames && rate > thresholdAccept[frameCount])
                            {
                                matchFound = true;
                                break;
                            }

                            if (nextIndexIncrement == 0)
                            {
                                // We reached the end of a hash list
                                break; // Break the while loop
                            }
                        }

                        if (matchFound)
                        {
                            matches.Add(new Match {
                                Similarity = 1f / numTried * numMatched,
                                Track1     = entry1.AudioTrack,
                                Track1Time = SubFingerprintIndexToTimeSpan(entry1.Index),
                                Track2     = entry2.AudioTrack,
                                Track2Time = SubFingerprintIndexToTimeSpan(entry2.Index),
                                Source     = matchSourceName
                            });
                        }
                    }
                }
            }

            return(matches);
        }