docstr: Finds hash matches that align in time with other matches and finds
  consensus about which hashes are "true" signal from the audio.
  
  :param matches: matches from the database
  :param dedup_hashes: dictionary containing the hashes matched without duplicates for each song
  (key is the song id).
  :param queried_hashes: amount of hashes sent for matching against the db
  :param topn: number of results being returned back.
  :return: a list of dictionaries (based on topn) with match information.
docstr_tok: ["Finds","hash","matches","that","align","in","time","with","other","matches","and","finds","consensus","about","which","hashes","are","``","true","''","signal","from","the","audio",".",":","param","matches",":","matches","from","the","database",":","param","dedup_hashes",":","dictionary","containing","the","hashes","matched","without","duplicates","for","each","song","(","key","is","the","song","id",")",".",":","param","queried_hashes",":","amount","of","hashes","sent","for","matching","against","the","db",":","param","topn",":","number","of","results","being","returned","back",".",":","return",":","a","list","of","dictionaries","(","based","on","topn",")","with","match","information","."]
code: def align_matches(
      self,
      matches: List[Tuple[int, int]],
      dedup_hashes: Dict[str, int],
      queried_hashes: int,
      topn: int = TOPN,
  ) -> List[Dict[str, any]]:
      """
      Finds hash matches that align in time with other matches and finds
      consensus about which hashes are "true" signal from the audio.
  
      :param matches: matches from the database
      :param dedup_hashes: dictionary containing the hashes matched without duplicates for each song
      (key is the song id).
      :param queried_hashes: amount of hashes sent for matching against the db
      :param topn: number of results being returned back.
      :return: a list of dictionaries (based on topn) with match information.
      """
      # count offset occurrences per song and keep only the maximum ones.
      sorted_matches = sorted(matches, key=lambda m: (m[0], m[1]))
      counts = [
          (*key, len(list(group)))
          for key, group in groupby(
              sorted_matches, key=lambda m: (m[0], m[1])
          )
      ]
      songs_matches = sorted(
          [
              max(list(group), key=lambda g: g[2])
              for key, group in groupby(
                  counts, key=lambda count: count[0]
              )
          ],
          key=lambda count: count[2],
          reverse=True,
      )
  
      songs_result = []
      for song_id, offset, _ in songs_matches[
          0:topn
      ]:  # consider topn elements in the result
          song = self.db.get_song_by_id(song_id)
  
          song_name = song.get(SONG_NAME, None)
          song_hashes = song.get(FIELD_TOTAL_HASHES, None)
          nseconds = round(
              float(offset)
              / DEFAULT_FS
              * DEFAULT_WINDOW_SIZE
              * DEFAULT_OVERLAP_RATIO,
              5,
          )
          hashes_matched = dedup_hashes[song_id]
  
          song = {
              SONG_ID: song_id,
              SONG_NAME: song_name.encode("utf8"),
              INPUT_HASHES: queried_hashes,
              FINGERPRINTED_HASHES: song_hashes,
              HASHES_MATCHED: hashes_matched,
              # Percentage regarding hashes matched vs hashes from the input.
              INPUT_CONFIDENCE: round(
                  hashes_matched / queried_hashes, 2
              ),
              # Percentage regarding hashes matched vs hashes fingerprinted in the db.
              FINGERPRINTED_CONFIDENCE: round(
                  hashes_matched / song_hashes, 2
              ),
              OFFSET: offset,
              OFFSET_SECS: nseconds,
              FIELD_FILE_SHA1: song.get(FIELD_FILE_SHA1, None).encode(
                  "utf8"
              ),
          }
  
          songs_result.append(song)
  
      return songs_result
  
imports: import multiprocessing
  import os
  import sys
  import traceback
  from itertools import groupby
  from time import time
  from typing import Dict, List, Tuple
  import dejavu.logic.decoder
  from dejavu.base_classes.base_database import get_database
  from dejavu.config.settings import DEFAULT_FS, DEFAULT_OVERLAP_RATIO, DEFAULT_WINDOW_SIZE, FIELD_FILE_SHA1, FIELD_TOTAL_HASHES, FINGERPRINTED_CONFIDENCE, FINGERPRINTED_HASHES, HASHES_MATCHED, INPUT_CONFIDENCE, INPUT_HASHES, OFFSET, OFFSET_SECS, SONG_ID, SONG_NAME, TOPN
  from dejavu.logic.fingerprint import fingerprint
label_desc: Use image node_id 9 for calling the Dejavu obj's underlying member method code with example usage: obj.align_matches(matches, dedup_hashes, queried_hashes, topn) and returns: songs_result