def __init__(self, folder, title, artist, numberOfsongs): self.length = numberOfsongs self.dirs = os.listdir('./'+folder) self.db = SongDatabase(folder,'tune_pickle') self.usable_songs = self.db.usable_songs() random.shuffle(self.usable_songs) self.build_playlist(title, artist, self.length)
"""To populate the database""" from database import SongDatabase db = SongDatabase("song_test","tune_pickle") db.reset_db() db.populate_db() db.print_db()
class Playlist(): def __init__(self, folder, title, artist, numberOfsongs): self.length = numberOfsongs self.dirs = os.listdir('./'+folder) self.db = SongDatabase(folder,'tune_pickle') self.usable_songs = self.db.usable_songs() random.shuffle(self.usable_songs) self.build_playlist(title, artist, self.length) def build_playlist(self, title, artist, numberOfSongs): """Using the base song, build a playlist of songs with scores and tempo compatible with the base song""" base_song = self.db.get_entry_by_name(title,artist) print '\nAdding %s to playlist' % base_song['Title'] if base_song['Usable']: self.baseSong = Tune(base_song['File_Path'], base_song['Artist'], base_song['Title'], base_song['Tempo'], self.db.get_pickle(base_song['Pickle_Path'])) self.playlist = [self.baseSong] self.added = [base_song['File_Path']] score_board = {} # Store songs that are compatible but at the time outside the tempo range not_compatible = {} # Store rejected songs i = 0 # index of item in song folder/directory k = 0 # keeps track of no. of times program has gone through the entire directory. Goes through twice just in case. while len(self.playlist) < numberOfSongs and k < 2: try: self.max_tempo = max([tune.bpm for tune in self.playlist]) self.min_tempo = min([tune.bpm for tune in self.playlist]) try: new_song = self.usable_songs[i] except IndexError: i = 0 k += 1 # If current song is the original song (in that same folder), skip if new_song['File_Path'] == base_song['File_Path']: i += 1 # Else searches the memoized scoreboard to see if it is there. If so # check if it is within the tempo range, if so add it to the playlist. elif new_song['File_Path'] in score_board: if abs(self.max_tempo - score_board[new_song['File_Path']]) <= TEMPO_THRESHOLD \ or abs(self.min_tempo - score_board[new_song['File_Path']]) <= TEMPO_THRESHOLD: self.add(new_song) score_board.pop(new_song['File_Path']) i = 0 else: i += 1 elif new_song['File_Path'] in not_compatible: i += 1 elif new_song: score, withinTempoRange = self.compare_songs(base_song, new_song) if score <= SCORE_THRESHOLD and withinTempoRange \ and new_song['File_Path'] not in self.added: self.add(new_song) i = 0 # Else memoize it in the scoreboard if it is not there already elif score <= SCORE_THRESHOLD \ and new_song['File_Path'] not in score_board \ and new_song['File_Path'] not in self.added: score_board[new_song['File_Path']] = new_song['Tempo'] else: not_compatible[new_song['File_Path']] = 0 except util.EchoNestAPIError: print '\nAPI rate limit has been exceeded. The program will resume in 60 seconds.\n' time.sleep(60) else: print 'Try another song.' def add(self, new_song): """Add a song to the playlist""" tune = Tune(new_song['File_Path'], new_song['Artist'], new_song['Title'], new_song['Tempo'], self.db.get_pickle(new_song['Pickle_Path'])) self.playlist.append(tune) self.added.append(new_song['File_Path']) print "Adding %s to playlist" %new_song['Title'] def sort_playlist(self): """Sort playlist in order of bpm from lowest to highest""" new = [] for i in range(len(self.playlist)): item = min(self.playlist, key=lambda x: x.bpm) new.append(item) self.playlist.remove(item) self.playlist = new def compare_songs(self, baseSong, otherSong): """Computes compatibility score between baseSong and otherSong""" score = 0 # based on danceability, liveness, energy and tempo for i in ['Danceability', 'Liveness', 'Energy']: score += abs(baseSong[i] - otherSong[i]) # checks if candidate song is within tempo range of the last song in the playlist if abs(self.max_tempo - otherSong['Tempo']) <= TEMPO_THRESHOLD \ or abs(self.min_tempo - otherSong['Tempo']) <= TEMPO_THRESHOLD: return score, True else: return score, False def splice_songs(self): """Add the following information to each song in the playlist: its position in the playlist (start, middle, end), its starting bar and its ending bar""" ordering = ['start'] + ['middle']*(len(self.playlist)-2) + ['end'] rp = [0] * len(self.playlist) for i in range(len(self.playlist)): rp[i] = [self.playlist[i], ordering[i], 0, 0] for i in rp: i[2], i[3] = i[0].choose_jump_point(position=i[1]) self.playlist = rp def mix_songs(self): """Splice the bars lists of the songs in the playlist according to the results returned by choose_jump_point() for each song, and add the transition between each two songs.""" output_song = [] p = self.playlist switch_durations = [] # Complicated for loop to put the output song together for i in range(len(p)): if p[i][1] == 'start': add = p[i][0].bars[: p[i][3]-(TRAN_BARS-1)] #from 0 to end-2 output_song += add switch_durations.append(sum([j.duration for j in add])) if p[i][1] == 'middle': add = p[i][0].bars[p[i][2]+TRAN_BARS : p[i][3]-(TRAN_BARS-1)] #from start+3 to end-2 output_song += add switch_durations.append(sum([j.duration for j in add])) if p[i][1] == 'end': add = p[i][0].bars[p[i][2]+TRAN_BARS :] #from start+3 output_song += add switch_durations.append(sum([j.duration for j in add])) try: trans = make_transition(p[i], p[i+1]) output_song += trans[0] switch_durations.append(trans[1]) except IndexError: pass self.mix = output_song return switch_durations def show(self): """Nicely display playlist""" j = 1 print '\n===== PLAYLIST ORDER =====\n' for i in self.playlist: print "%d. %s by %s" %(j, i[0].songName, i[0].artist) j += 1 print ''