98 lines
3.5 KiB
Python
Executable File
98 lines
3.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import logging
|
|
import math
|
|
import os
|
|
import sqlite3
|
|
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
|
|
if "XDG_DATA_HOME" in os.environ:
|
|
_BLISSIFY_DATA_HOME = os.path.expandvars("$XDG_DATA_HOME/blissify")
|
|
else:
|
|
_BLISSIFY_DATA_HOME = os.path.expanduser("~/.local/share/blissify")
|
|
|
|
|
|
def main():
|
|
db_path = os.path.join(_BLISSIFY_DATA_HOME, "db.sqlite3")
|
|
logging.debug("Using DB path: %s." % (db_path,))
|
|
conn = sqlite3.connect(db_path)
|
|
conn.row_factory = sqlite3.Row
|
|
conn.execute('pragma foreign_keys=ON')
|
|
cur = conn.cursor()
|
|
|
|
# Get cached distances from db
|
|
cur.execute("SELECT song1, song2, distance, similarity FROM distances")
|
|
cached_distances = cur.fetchall()
|
|
|
|
# Get all songs
|
|
cur.execute("SELECT id, tempo, amplitude, frequency, attack, filename FROM songs")
|
|
all_songs = cur.fetchall()
|
|
|
|
for i in range(len(all_songs)):
|
|
for j in range(i + 1, len(all_songs)):
|
|
song1 = all_songs[i]
|
|
song2 = all_songs[j]
|
|
is_cached = len([i for i in cached_distances
|
|
if(i["song1"] == song1["id"] and
|
|
i["song2"] == song2["id"]) or
|
|
(i["song1"] == song2["id"] and
|
|
i["song2"] == song1["id"])]) > 0
|
|
if is_cached:
|
|
# Pass pair if cached value is already there
|
|
continue
|
|
# Compute distance
|
|
distance = math.sqrt(
|
|
(song1["tempo"] - song2["tempo"])**2 +
|
|
(song1["amplitude"] - song2["amplitude"])**2 +
|
|
(song1["frequency"] - song2["frequency"])**2 +
|
|
(song1["attack"] - song2["attack"])**2
|
|
)
|
|
similarity = (
|
|
(song1["tempo"] * song2["tempo"] +
|
|
song1["amplitude"] * song2["amplitude"] +
|
|
song1["frequency"] * song2["frequency"] +
|
|
song1["attack"] * song2["attack"]) /
|
|
(
|
|
math.sqrt(
|
|
song1["tempo"]**2 +
|
|
song1["amplitude"]**2 +
|
|
song1["frequency"]**2 +
|
|
song1["attack"]**2) *
|
|
math.sqrt(
|
|
song2["tempo"]**2 +
|
|
song2["amplitude"]**2 +
|
|
song2["frequency"]**2 +
|
|
song2["attack"]**2)
|
|
)
|
|
)
|
|
|
|
logging.debug("Distance between %s and %s is (%f, %f)." %
|
|
(song1["filename"], song2["filename"], distance,
|
|
similarity))
|
|
# Store distance in db cache
|
|
try:
|
|
logging.debug("Storing distance in database.")
|
|
conn.execute(
|
|
"INSERT INTO distances(song1, song2, distance, similarity) VALUES(?, ?, ?, ?)",
|
|
(song1["id"], song2["id"], distance, similarity))
|
|
conn.commit()
|
|
# Update cached_distances list
|
|
cached_distances.append({
|
|
"song1": song1["id"],
|
|
"song2": song2["id"],
|
|
"distance": distance,
|
|
"similarity": similarity
|
|
})
|
|
except sqlite3.IntegrityError:
|
|
logging.warning("Unable to insert distance in database.")
|
|
conn.rollback()
|
|
# Close connection
|
|
conn.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
main()
|
|
except KeyboardInterrupt:
|
|
pass
|