-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpyDJ.py
More file actions
153 lines (104 loc) · 3.82 KB
/
Copy pathpyDJ.py
File metadata and controls
153 lines (104 loc) · 3.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#!/usr/bin/env python
import os
import librosa
import numpy as np
from glob import glob
from pydub import AudioSegment
from sklearn.neighbors import NearestNeighbors
from tqdm import tqdm
import argparse
config = {'SR': 22050,
'OFFSET': 5,
'DURATION': 20,
'CROSSFADE': 10}
def load_audio(audioPath):
y, sr = librosa.load(audioPath, duration=config['DURATION'], sr=config['SR'])
audiosamples = config['SR'] * config['DURATION']
if y.shape[0] < audiosamples:
y = np.pad(y, (0, audiosamples), 'constant')
y = y[:audiosamples]
return y, sr
def get_tempo(y, sr):
tempo, beats = librosa.beat.beat_track(y=y, sr=sr)
return tempo, beats
def get_mfcc(y, sr):
mfccs = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=20)
return mfccs
def compute_features(inputDir, audiofiles):
features = []
tempos = []
songs_list = []
for audio in tqdm(audiofiles):
try:
y, sr = load_audio(os.path.join(inputDir, audio))
tempo, _ = get_tempo(y, sr)
mfccs = get_mfcc(y,sr)
tempos.append(tempo)
mfccs = np.ravel(mfccs)
features.append(mfccs)
songs_list.append(audio)
except Exception as e:
print(e)
features = np.array(features)
return features, songs_list, tempos
def computeNN(features):
nbrs = NearestNeighbors(n_neighbors=features.shape[0], algorithm='ball_tree').fit(features)
return nbrs
def get_min_bpm_index(tempos):
min_bpm_index = tempos.index(min(tempos))
return min_bpm_index
def create_playlist(results, outputDir):
playlist_songs = [AudioSegment.from_file(audio_file) for audio_file in results]
first_song = playlist_songs.pop(0)
beginning_of_song = first_song.fade_in(2000)
playlist = beginning_of_song
CROSSFADE = config['CROSSFADE']
for song in tqdm(playlist_songs):
if len(song) <= config['CROSSFADE'] * 1000:
CROSSFADE = 1
# We don't want an abrupt stop at the end, so let's do a 10 second crossfades
playlist = playlist.append(song, crossfade=(CROSSFADE * 1000))
# fade out the end of the last song
playlist = playlist.fade_out(30)
# mixtape lenght ( len(audio_segment) returns milliseconds )
playlist_length = len(playlist) / (1000*60)
print('playlist duration:', playlist_length)
# save
out_f = open(os.path.join(outputDir, 'playlist.mp3'), 'wb')
playlist.export(out_f, format='wav')
return
def main(inputDir, outputDir, neighs):
audiofiles = os.listdir(inputDir)
if neighs is None:
neighs = 10
if neighs < len(audiofiles):
neighs = len(audiofiles)
print('Computing features..')
features, songs_list, tempos = compute_features(inputDir, audiofiles)
print('Computing NNs')
nbrs = computeNN(features)
# get song with lowest tempo
min_bpm_index = get_min_bpm_index(tempos)
# start
distances, indices = nbrs.kneighbors(features[min_bpm_index].reshape(1, -1))
results = []
for i in indices[0]:
results.append(songs_list[i])
results = results[:neighs]
print('PLAYLIST')
for i in results:
print(i)
results = [os.path.join(inputDir, i) for i in results]
print('Creating playlist..')
create_playlist(results, outputDir)
return
if __name__ == '__main__':
parser=argparse.ArgumentParser(description='pyDJ - automatic mixtapes generation')
parser.add_argument('-i', '--inputDir', type=str, required=True)
parser.add_argument('-o', '--outputDir', type=str, required=True)
parser.add_argument('-n', '--neighs', default=None, type=int, required=False)
args = parser.parse_args()
indir = args.inputDir
outdir = args.outputDir
n = args.neighs
main(indir, outdir, n)