Also bump Bliss version
This commit is contained in:
Lucas Verney 2017-01-02 19:09:54 +01:00
parent 56108cea38
commit 6edc90c287
1 changed files with 63 additions and 103 deletions

View File

@ -103,60 +103,63 @@ if "XDG_DATA_HOME" in os.environ:
else: else:
_BLISSIFY_DATA_HOME = os.path.expanduser("~/.local/share/blissify") _BLISSIFY_DATA_HOME = os.path.expanduser("~/.local/share/blissify")
# Distance between two songs
def distance(x, y): def distance(x, y):
distance = math.sqrt( """
Compute the distance between two songs.
Params:
- x: First song dict
- y: Second song dict
Returns: The cartesian distance between the two songs.
"""
return math.sqrt(
(x["tempo"] - y["tempo"])**2 + (x["tempo"] - y["tempo"])**2 +
(x["amplitude"] - y["amplitude"])**2 + (x["amplitude"] - y["amplitude"])**2 +
(x["frequency"] - y["frequency"])**2 + (x["frequency"] - y["frequency"])**2 +
(x["attack"] - y["attack"])**2) (x["attack"] - y["attack"])**2
return distance )
# Distance between a set and a point
def distance_set(X, y):
temp_distance = distance(X[0], y);
for song in X:
a = distance(song, y);
temp_distance = a if a < temp_distance else temp_distance;
return temp_distance;
# Hausdorff distance between two sets
def hauff_distance_sets(X, Y):
temp_distance = distance_set(X, Y[0]);
for song in X:
a = distance_set(Y, song);
temp_distance = a if a > temp_distance else temp_distance;
for song in Y:
a = distance_set(X, song);
temp_distance = a if a > temp_distance else temp_distance;
return temp_distance
def mean_song(X): def mean_song(X):
count = 0; """
result = { 'tempo': 0, 'amplitude': 0, 'frequency':0, 'attack':0 } Compute a "mean" song for a given iterable of song dicts.
Params:
- X: An iterable of song dicts.
Returns: A "mean" song, whose features are the mean features of the songs
in the iterable.
"""
count = 0
result = {'tempo': 0, 'amplitude': 0, 'frequency': 0, 'attack': 0}
for song in X: for song in X:
result["tempo"] += song["tempo"]; result["tempo"] += song["tempo"]
result["amplitude"] += song["amplitude"]; result["amplitude"] += song["amplitude"]
result["frequency"] += song["frequency"]; result["frequency"] += song["frequency"]
result["attack"] += song["attack"]; result["attack"] += song["attack"]
count = count + 1; count = count + 1
result["tempo"] /= count; result["tempo"] /= count
result["amplitude"] /= count; result["amplitude"] /= count
result["frequency"] /= count; result["frequency"] /= count
result["attack"] /= count; result["attack"] /= count
return result; return result
# Custom distance between two sets
def distance_sets(X, Y): def distance_sets(X, Y):
a = mean_song(X); """
b = mean_song(Y); Compute the distance between two iterables of song dicts, defined as the
distance between the two mean songs of the iterables.
return distance(a, b); Params:
- X: First iterable of song dicts.
- Y: First iterable of song dicts.
Returns: The distance between the two iterables.
"""
return distance(mean_song(X), mean_song(Y))
def main_album(queue_length):
def _init():
# Get MPD connection settings # Get MPD connection settings
try: try:
mpd_host = os.environ["MPD_HOST"] mpd_host = os.environ["MPD_HOST"]
@ -199,6 +202,7 @@ def main_album(queue_length):
all_songs = [x["file"] for x in client.listall() if "file" in x] all_songs = [x["file"] for x in client.listall() if "file" in x]
current_song = random.choice(all_songs) current_song = random.choice(all_songs)
client.add(current_song) client.add(current_song)
logging.info("Currently played song is %s." % (current_song,)) logging.info("Currently played song is %s." % (current_song,))
# Get current song coordinates # Get current song coordinates
@ -211,17 +215,23 @@ def main_album(queue_length):
client.disconnect() client.disconnect()
sys.exit(1) sys.exit(1)
return client, conn, cur, current_song_coords
def main_album(queue_length):
client, conn, cur, current_song_coords = _init()
for i in range(queue_length): for i in range(queue_length):
# No cache management # No cache management
# Get all songs from the current album # Get all songs from the current album
album = current_song_coords["album"]; album = current_song_coords["album"]
cur.execute("SELECT id, tempo, amplitude, frequency, attack, filename, album FROM songs WHERE album=?", (album,)) cur.execute("SELECT id, tempo, amplitude, frequency, attack, filename, album FROM songs WHERE album=?", (album,))
target_album_set = cur.fetchall(); target_album_set = cur.fetchall()
# Get all other songs # Get all other songs
cur.execute("SELECT id, tempo, amplitude, frequency, attack, filename, album FROM songs ORDER BY album") cur.execute("SELECT id, tempo, amplitude, frequency, attack, filename, album FROM songs ORDER BY album")
tmp_song_data = cur.fetchone() tmp_song_data = cur.fetchone()
shortest_distance = -1 shortest_distance = -1
# Check the best suitable album # Check the best suitable album
while tmp_song_data: while tmp_song_data:
@ -233,18 +243,18 @@ def main_album(queue_length):
# Get all songs from the current temporary album # Get all songs from the current temporary album
while tmp_song_data: while tmp_song_data:
if (current_album_set[i]["album"] == tmp_song_data["album"]): if (current_album_set[i]["album"] == tmp_song_data["album"]):
current_album_set.append(tmp_song_data); current_album_set.append(tmp_song_data)
else: else:
break; break
tmp_song_data = cur.fetchone() tmp_song_data = cur.fetchone()
i = i + 1 i = i + 1
# Skip current album and already processed albums # Skip current album and already processed albums
if ( (current_album_set[0]["album"] != target_album_set[0]["album"]) and if((current_album_set[0]["album"] != target_album_set[0]["album"]) and
not (("file: %s" % (current_album_set[0]["filename"],)) in client.playlist()) ): not (("file: %s" % (current_album_set[0]["filename"],)) in client.playlist())):
tmp_distance = distance_sets(current_album_set, target_album_set) tmp_distance = distance_sets(current_album_set, target_album_set)
if tmp_distance < shortest_distance or shortest_distance == -1: if tmp_distance < shortest_distance or shortest_distance == -1:
shortest_distance = tmp_distance shortest_distance = tmp_distance
closest_album = current_album_set; closest_album = current_album_set
logging.info("Closest album found is \"%s\". Distance is %f." % (closest_album[0]["album"], shortest_distance)) logging.info("Closest album found is \"%s\". Distance is %f." % (closest_album[0]["album"], shortest_distance))
for song in closest_album: for song in closest_album:
@ -255,59 +265,9 @@ def main_album(queue_length):
client.close() client.close()
client.disconnect() client.disconnect()
def main_single(queue_length): def main_single(queue_length):
# Get MPD connection settings client, conn, cur, current_song_coords = _init()
try:
mpd_host = os.environ["MPD_HOST"]
try:
mpd_password, mpd_host = mpd_host.split("@")
except ValueError:
mpd_password = None
except KeyError:
mpd_host = "localhost"
mpd_password = None
try:
mpd_port = os.environ["MPD_PORT"]
except KeyError:
mpd_port = 6600
# Connect to MPD
client = PersistentMPDClient(host=mpd_host, port=mpd_port)
if mpd_password is not None:
client.password(mpd_password)
# Connect to db
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()
# Ensure random is not enabled
status = client.status()
if int(status["random"]) != 0:
logging.warning("Random mode is enabled. Are you sure you want it?")
# Take the last song from current playlist and iterate from it
playlist = client.playlist()
if len(playlist) > 0:
current_song = playlist[-1].replace("file: ", "").rstrip()
# If current playlist is empty
else:
# Add a random song to start with
all_songs = [x["file"] for x in client.listall() if "file" in x]
current_song = random.choice(all_songs)
client.add(current_song)
logging.info("Currently played song is %s." % (current_song,))
# Get current song coordinates
cur.execute("SELECT id, tempo, amplitude, frequency, attack, filename FROM songs WHERE filename=?", (current_song,))
current_song_coords = cur.fetchone()
if current_song_coords is None:
logging.error("Current song %s is not in db. You should update the db." %
(current_song,))
client.close()
client.disconnect()
sys.exit(1)
for i in range(queue_length): for i in range(queue_length):
# Get cached distances from db # Get cached distances from db