All Downloads are FREE. Search and download functionalities are using the official Maven repository.

angry1980.audio.similarity.HashErrorRatesCalculator Maven / Gradle / Ivy

package angry1980.audio.similarity;

import angry1980.audio.dao.FingerprintDAO;
import angry1980.audio.dao.TrackDAO;
import angry1980.audio.model.HashFingerprint;
import angry1980.audio.model.FingerprintType;
import angry1980.audio.model.TrackSimilarity;
import org.apache.commons.lang3.ArrayUtils;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class HashErrorRatesCalculator implements Calculator {

    private static final double positiveLimit = 0.8;

    private FingerprintType type;
    private TrackDAO trackDAO;
    private FingerprintDAO fingerprintDAO;
    private int batchSize;
    private int errorLimit;

    public HashErrorRatesCalculator(FingerprintType type, TrackDAO trackDAO, FingerprintDAO fingerprintDAO){
        this(type, trackDAO, fingerprintDAO, 25, 8);
    }

    public HashErrorRatesCalculator(FingerprintType type, TrackDAO trackDAO, FingerprintDAO fingerprintDAO, int batchSize, int errorLimit) {
        this.type = type;
        this.trackDAO = Objects.requireNonNull(trackDAO);
        this.fingerprintDAO = Objects.requireNonNull(fingerprintDAO);
        this.batchSize = batchSize;
        this.errorLimit = errorLimit;
    }

    @Override
    public List calculate(HashFingerprint fingerprint) {
        return trackDAO.get(fingerprint.getTrackId())
                .map(track -> trackDAO.findByCluster(track.getCluster()).stream().mapToLong(t -> t.getId()).toArray())
                .map(fingerprintDAO::findByTrackIds)
                .map(list -> list.stream()
                    .filter(fp -> fp.getTrackId() != fingerprint.getTrackId())
                    .map(fp -> new TrackSimilarity(
                            fingerprint.getTrackId(),
                            fp.getTrackId(),
                            calculate(fingerprint.getHashes(), fp.getHashes()),
                            type)
                    ).filter(ts -> ts.getValue() > 20)
                    .collect(Collectors.toList())
                ).orElseGet(() -> Collections.emptyList())
        ;
    }

    private int calculate(int[] source, int[] other) {
        int counter = 0;
        if(other == null || source == null){
            return counter;
        }
        int batchSize = Math.min(this.batchSize, other.length - 1);
        for (int begin = 0; begin < source.length; begin += batchSize){
            int end = Math.min(begin + batchSize, source.length);
            int[] batch = ArrayUtils.subarray(source, begin, end);
            int limit = (int) (batch.length * positiveLimit);
            int s = 0;
            for(int i = 0; i < other.length - batch.length; i++){
                s = Math.max(s, check(batch, ArrayUtils.subarray(other, i, i + batch.length)));
                if(s == batch.length){
                    break;
                }
            }
            if(s > limit){
                counter += s;
            }
        }
        return counter;
    }


    private int check(int[] source, int[] other){
        int counter = 0;
        for(int i = 0; i < source.length && i < other.length; i++){
            int errors = Integer.bitCount(source[i] ^ other[i]);
            if(errors <= errorLimit){
                counter++;
            }
        }
        return counter;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy