text: #!/usr/bin/env python
  # encoding: utf-8
  import librosa
  from numpy import random
  import numpy
  import os.path
  
  
  def hash_func(vecs, projections):
      bools = numpy.dot(vecs, projections.T) > 0
      return [bool2int(bool_vec) for bool_vec in bools]
  
  
  def bool2int(x):
      y = 0
      for i,j in enumerate(x):
          if j: y += 1<<i
      return y
  
  
  class Table:
      def __init__(self, hash_size, dim):
          self.table = dict()
          self.hash_size = hash_size
          self.projections = random.randn(self.hash_size, dim)
  
      def add(self, vecs, label):
          entry = {'label': label}
          hashes = hash_func(vecs, self.projections)
          for h in hashes:
              if h in self.table:
                  self.table[h].append(entry)
              else:
                  self.table[h] = [entry]
  
      def query(self, vecs):
          hashes = hash_func(vecs, self.projections)
          results = list()
          for h in hashes:
              if h in self.table:
                  results.extend(self.table[h])
          return results
  
  
  class LSH:
  
      def __init__(self, dim):
          self.num_tables = 4
          self.hash_size = 8
          self.tables = list()
          for i in range(self.num_tables):
              self.tables.append(Table(self.hash_size, dim))
  
      def add(self, vecs, label):
          for table in self.tables:
              table.add(vecs, label)
  
      def query(self, vecs):
          results = list()
          for table in self.tables:
              results.extend(table.query(vecs))
          return results
  
      def describe(self):
          for table in self.tables:
              print(table.table)
  
  
  class MusicSearch:
  
      def __init__(self, training_files):
          self.frame_size = 4096
          self.hop_size = 4000
          self.fv_size = 12
          self.lsh = LSH(self.fv_size)
          self.training_files = training_files
          self.num_features_in_file = dict()
          for f in self.training_files:
              self.num_features_in_file[f] = 0
  
      def train(self):
          for filepath in self.training_files:
              x, fs = librosa.load(filepath)
              features = librosa.feature.chroma_stft(x, fs, n_fft=self.frame_size, hop_length=self.hop_size).T
              self.lsh.add(features, filepath)
              self.num_features_in_file[filepath] += len(features)
  
      def query(self, filepath):
          x, fs = librosa.load(filepath)
          features = librosa.feature.chroma_stft(x, fs, n_fft=self.frame_size, hop_length=self.hop_size).T
          results = self.lsh.query(features)
          print ('num results', len(results))
  
          counts = dict()
          for r in results:
              if r['label'] in counts:
                  counts[r['label']] += 1
              else:
                  counts[r['label']] = 1
          for k in counts:
              counts[k] = float(counts[k])/self.num_features_in_file[k]
          return counts
  
  
  if __name__ == '__main__':
      training_dir = '../../audio_resources/'
      training_files = [os.path.join(training_dir, f) for f in os.listdir(training_dir)]
      bool2int([False, True, False, True])
  
      X = random.randn(10, 100)
      P = random.randn(3, 100)
      hash_func(X, P)
  
      print('Learning all music')
      ms = MusicSearch(training_files)
      ms.train()
  
      print('Querying with test file')
      test_file = '../../audio_resources/metal0.mp3'
      results = ms.query(test_file)
  
      for r in sorted(results, key=results.get, reverse=True):
          print(r, results[r])
  