├── .gitignore ├── README.md ├── app.py ├── requirements.txt ├── src ├── extract_metadata.py ├── feature_extraction.py ├── playlist_metadata.py ├── predict_mood.py └── train_model.py └── templates └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | src/__pycache__ 2 | 3 | data/labeled/labels_with_artists.csv 4 | data/features.csv 5 | data/music_mood_model.pkl 6 | 7 | static 8 | data/labeled/happy 9 | data/labeled/romantic 10 | data/labeled/sad 11 | venv -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Music Mood Playlist Filter 2 | Project Overview 3 | The Music Mood Playlist Filter is an innovative tool that allows users to filter and curate music playlists based on advanced auditory features such as pitch and artist. This solution extends beyond traditional playlist filtering by enabling users to refine their music experience based on the specific pitch of songs, offering a unique way to explore and enjoy music. -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request, redirect, url_for 2 | import os 3 | import pandas as pd 4 | import urllib.parse 5 | import subprocess 6 | 7 | 8 | app = Flask(__name__) 9 | app.config['UPLOAD_FOLDER'] = 'static/uploads' 10 | app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16 MB limit 11 | 12 | if not os.path.exists(app.config['UPLOAD_FOLDER']): 13 | os.makedirs(app.config['UPLOAD_FOLDER']) 14 | 15 | # Run the metadata extraction script 16 | def run_metadata_script(): 17 | script_path = 'I:/Music-classifier/src/playlist_metadata.py' # Adjust this path 18 | subprocess.run(['venv/Scripts/python', script_path], check=True) 19 | 20 | # Load artist names from the CSV, ensuring unique and individual artist names 21 | def get_artists(): 22 | df = pd.read_csv('./static/labels_with_playlist_artists.csv') 23 | unique_artists = sorted(set(artist.strip() for artists in df['artist'] for artist in artists.split(','))) 24 | return unique_artists 25 | 26 | @app.route('/') 27 | def index(): 28 | files = os.listdir(app.config['UPLOAD_FOLDER']) 29 | artists = get_artists() # Get the list of unique artists 30 | return render_template('index.html', files=files, artists=artists) 31 | 32 | @app.route('/upload', methods=['POST']) 33 | def upload_file(): 34 | if 'file' not in request.files: 35 | return redirect(request.url) 36 | file = request.files['file'] 37 | if file.filename == '': 38 | return redirect(request.url) 39 | if file: 40 | filename = file.filename 41 | file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) 42 | file.save(file_path) 43 | 44 | # Run the script after saving the file 45 | run_metadata_script() 46 | refresh_song_data() 47 | 48 | return redirect(url_for('index')) 49 | 50 | @app.route('/remove/', methods=['POST']) 51 | def remove_file(filename): 52 | decoded_filename = urllib.parse.unquote(filename) 53 | file_path = os.path.join(app.config['UPLOAD_FOLDER'], decoded_filename) 54 | if os.path.exists(file_path): 55 | os.remove(file_path) 56 | 57 | # Run the script after removing the file 58 | run_metadata_script() 59 | refresh_song_data() 60 | 61 | return redirect(url_for('index')) 62 | 63 | song_data = pd.read_csv('I:/Music-classifier/static/labels_with_playlist_artists.csv') 64 | 65 | def refresh_song_data(): 66 | global song_data 67 | song_data = pd.read_csv('I:/Music-classifier/static/labels_with_playlist_artists.csv') 68 | 69 | @app.route('/apply_filters', methods=['POST']) 70 | def apply_filters(): 71 | # Get selected moods and singers from the form 72 | selected_moods = request.form.getlist('emotion') 73 | selected_singers = request.form.getlist('singer') 74 | 75 | # Filter the DataFrame based on the selected options 76 | filtered_data = song_data.copy() 77 | 78 | # Apply both filters if selections are made 79 | if selected_moods and selected_singers: 80 | filtered_data = filtered_data[ 81 | (filtered_data['mood'].isin(selected_moods)) & 82 | (filtered_data['artist'].apply(lambda artists: any(singer in artists for singer in selected_singers))) 83 | ] 84 | elif selected_moods: # If only moods are selected 85 | filtered_data = filtered_data[filtered_data['mood'].isin(selected_moods)] 86 | elif selected_singers: # If only singers are selected 87 | filtered_data = filtered_data[filtered_data['artist'].apply(lambda artists: any(singer in artists for singer in selected_singers))] 88 | 89 | 90 | 91 | # Extract only the filenames of the filtered songs 92 | filtered_files = filtered_data['filename'].tolist() 93 | 94 | # Reuse the index view but with filtered files 95 | artists = get_artists() # Get the list of unique artists again for the filters 96 | return render_template('index.html', files=filtered_files, artists=artists) 97 | 98 | if __name__ == '__main__': 99 | app.run(debug=True) 100 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Midhun31/Music-classifier-Machine-Learning/89427a87862583a9a7ad6b402ed0122e94d68459/requirements.txt -------------------------------------------------------------------------------- /src/extract_metadata.py: -------------------------------------------------------------------------------- 1 | import os 2 | from mutagen.id3 import ID3 3 | 4 | def extract_artist(file_path): 5 | try: 6 | audio = ID3(file_path) 7 | artist_tag = audio.get('TPE1') # 'TPE1' is the ID3 tag for artist 8 | artist = artist_tag.text[0] if artist_tag else 'Unknown Artist' 9 | if ' -' in artist: 10 | artist = artist.split(' -')[0] 11 | return artist 12 | except Exception as e: 13 | return f"Error: {e}" 14 | 15 | # Directory containing labeled audio files 16 | base_dir = 'data/labeled/' 17 | 18 | # Initialize lists to store file names, their corresponding labels, and artists 19 | file_names = [] 20 | labels = [] 21 | artists = [] 22 | 23 | # Iterate over each mood directory 24 | for label in os.listdir(base_dir): 25 | mood_dir = os.path.join(base_dir, label) 26 | if os.path.isdir(mood_dir): 27 | # Iterate over each audio file in the mood directory 28 | for file_name in os.listdir(mood_dir): 29 | if file_name.endswith('.mp3'): # Adjust if using other formats 30 | file_path = os.path.join(mood_dir, file_name) 31 | file_names.append(file_name) 32 | labels.append(label) 33 | 34 | # Extract artist's name 35 | artist = extract_artist(file_path) 36 | artists.append(artist) 37 | 38 | # Create DataFrame and save to CSV 39 | import pandas as pd 40 | df = pd.DataFrame({ 41 | 'filename': file_names, 42 | 'mood': labels, 43 | 'artist': artists 44 | }) 45 | csv_file = os.path.join(base_dir, 'labels_with_artists.csv') 46 | df.to_csv(csv_file, index=False) 47 | 48 | print(f"CSV file created at: {csv_file}") 49 | -------------------------------------------------------------------------------- /src/feature_extraction.py: -------------------------------------------------------------------------------- 1 | import os 2 | import librosa 3 | import pandas as pd 4 | import numpy as np 5 | 6 | # Directory containing labeled audio files 7 | base_dir = './data/labeled/' # Adjust this path based on your actual directory structure 8 | 9 | # Initialize lists to store features and labels 10 | features = [] 11 | labels = [] 12 | 13 | # Iterate over each mood directory 14 | for label in os.listdir(base_dir): 15 | mood_dir = os.path.join(base_dir, label) 16 | if os.path.isdir(mood_dir): 17 | # Iterate over each audio file in the mood directory 18 | for file_name in os.listdir(mood_dir): 19 | if file_name.endswith('.wav') or file_name.endswith('.mp3'): 20 | file_path = os.path.join(mood_dir, file_name) 21 | try: 22 | # Load the audio file 23 | y, sr = librosa.load(file_path, sr=None) 24 | 25 | # Extract features 26 | mfccs = np.mean(librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13).T, axis=0) 27 | chroma = np.mean(librosa.feature.chroma_stft(y=y, sr=sr).T, axis=0) 28 | spectral_contrast = np.mean(librosa.feature.spectral_contrast(y=y, sr=sr).T, axis=0) 29 | zero_crossing_rate = np.mean(librosa.feature.zero_crossing_rate(y=y).T, axis=0) 30 | rms = np.mean(librosa.feature.rms(y=y).T, axis=0) 31 | spectral_rolloff = np.mean(librosa.feature.spectral_rolloff(y=y, sr=sr).T, axis=0) 32 | 33 | # Combine all features 34 | feature_vector = np.concatenate([ 35 | mfccs, 36 | chroma, 37 | spectral_contrast, 38 | zero_crossing_rate, 39 | rms, 40 | spectral_rolloff 41 | ]) 42 | 43 | # Append features and label 44 | features.append(feature_vector) 45 | labels.append(label) 46 | except Exception as e: 47 | print(f"Error processing {file_path}: {e}") 48 | 49 | # Create a DataFrame from the features and labels 50 | columns = [ 51 | 'mfcc_' + str(i) for i in range(13) 52 | ] + [ 53 | 'chroma_' + str(i) for i in range(12) 54 | ] + [ 55 | 'spectral_contrast_' + str(i) for i in range(7) 56 | ] + [ 57 | 'zero_crossing_rate' 58 | ] + [ 59 | 'rms' 60 | ] + [ 61 | 'spectral_rolloff' 62 | ] 63 | 64 | df = pd.DataFrame(features, columns=columns) 65 | df['mood'] = labels 66 | 67 | # Save the DataFrame to a CSV file 68 | features_csv_file = './data/features.csv' 69 | df.to_csv(features_csv_file, index=False) 70 | 71 | print(f"Features CSV file created at: {features_csv_file}") 72 | -------------------------------------------------------------------------------- /src/playlist_metadata.py: -------------------------------------------------------------------------------- 1 | import os 2 | from mutagen.id3 import ID3 3 | import pandas as pd 4 | from predict_mood import predict_mood 5 | 6 | def extract_artist(file_path): 7 | try: 8 | audio = ID3(file_path) 9 | artist_tag = audio.get('TPE1') # 'TPE1' is the ID3 tag for artist 10 | artist = artist_tag.text[0] if artist_tag else 'Unknown Artist' 11 | if ' -' in artist: 12 | artist = artist.split(' -')[0] 13 | return artist 14 | except Exception as e: 15 | return f"Error: {e}" 16 | 17 | # Directory containing the audio files 18 | base_dir = 'I:/Music-classifier/static/uploads/' 19 | 20 | # Initialize lists to store file names and their corresponding artists 21 | file_names = [] 22 | moods=[] 23 | artists = [] 24 | 25 | # Iterate over each audio file in the base directory 26 | for file_name in os.listdir(base_dir): 27 | if file_name.endswith('.mp3'): # Adjust if using other formats 28 | file_path = os.path.join(base_dir, file_name) 29 | file_names.append(file_name) 30 | 31 | # Extract artist's name 32 | artist = extract_artist(file_path) 33 | artists.append(artist) 34 | 35 | # Extract mood 36 | mood = predict_mood(file_path) 37 | moods.append(mood) 38 | 39 | # Create DataFrame and save to CSV (without the mood column) 40 | df = pd.DataFrame({ 41 | 'filename': file_names, 42 | 'mood':moods, 43 | 'artist': artists 44 | }) 45 | csv_file = os.path.join('I:/Music-classifier/static/', 'labels_with_playlist_artists.csv') 46 | df.to_csv(csv_file, index=False) 47 | 48 | print(f"CSV file created at: {csv_file}") 49 | -------------------------------------------------------------------------------- /src/predict_mood.py: -------------------------------------------------------------------------------- 1 | import librosa 2 | import numpy as np 3 | import joblib 4 | 5 | # Load the trained model 6 | model = joblib.load('I:/Music-classifier/data/music_mood_model.pkl') 7 | 8 | # Function to extract features from a new audio file 9 | def extract_features(file_path): 10 | try: 11 | # Load the audio file 12 | y, sr = librosa.load(file_path, sr=None) 13 | 14 | # Extract features 15 | mfccs = np.mean(librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13).T, axis=0) 16 | chroma = np.mean(librosa.feature.chroma_stft(y=y, sr=sr).T, axis=0) 17 | spectral_contrast = np.mean(librosa.feature.spectral_contrast(y=y, sr=sr).T, axis=0) 18 | zero_crossing_rate = np.mean(librosa.feature.zero_crossing_rate(y=y).T, axis=0) 19 | rms = np.mean(librosa.feature.rms(y=y).T, axis=0) 20 | spectral_rolloff = np.mean(librosa.feature.spectral_rolloff(y=y, sr=sr).T, axis=0) 21 | 22 | # Combine all features into a single feature vector 23 | feature_vector = np.concatenate([ 24 | mfccs, 25 | chroma, 26 | spectral_contrast, 27 | zero_crossing_rate, 28 | rms, 29 | spectral_rolloff 30 | ]) 31 | 32 | return feature_vector 33 | except Exception as e: 34 | print(f"Error extracting features from {file_path}: {e}") 35 | return None 36 | 37 | # Function to predict the mood of a new song 38 | def predict_mood(file_path): 39 | # Extract features from the new song 40 | features = extract_features(file_path) 41 | 42 | if features is not None: 43 | # Reshape the features to match the model's expected input format 44 | features = features.reshape(1, -1) 45 | 46 | # Predict the mood using the model 47 | prediction = model.predict(features) 48 | 49 | # Print the predicted mood 50 | return prediction[0] 51 | else: 52 | return "Could not predict mood due to feature extraction error." 53 | 54 | # Provide the path to your new song here 55 | new_song_path = 'I:/Music-classifier/static/uploads/Aaluma-Doluma.mp3' 56 | mood=predict_mood(new_song_path) 57 | print(mood) -------------------------------------------------------------------------------- /src/train_model.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from sklearn.model_selection import train_test_split 3 | from sklearn.ensemble import RandomForestClassifier 4 | from sklearn.metrics import accuracy_score, classification_report 5 | import joblib 6 | 7 | # Load the features 8 | data = pd.read_csv('I:/Music-classifier/data/features.csv') 9 | 10 | # Separate features and labels 11 | X = data.drop(columns=['mood']) 12 | y = data['mood'] 13 | 14 | # Split the data 15 | X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) 16 | 17 | # Train the model 18 | model = RandomForestClassifier(n_estimators=100, random_state=42) 19 | model.fit(X_train, y_train) 20 | 21 | # Evaluate the model 22 | y_pred = model.predict(X_test) 23 | print(f'Accuracy: {accuracy_score(y_test, y_pred)}') 24 | print(classification_report(y_test, y_pred)) 25 | 26 | # Save the model 27 | joblib.dump(model, 'I:/Music-classifier/data/music_mood_model.pkl') 28 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | My Playlist 8 | 117 | 123 | 124 | 125 | 126 |
127 |

My Playlist

128 | 129 |
130 |
131 | 132 | 133 | 134 |
135 |
136 | 137 | 138 | 139 |
140 |

Filter By

141 | 142 | 143 |
144 | 145 |
146 | Emotions 147 | 150 | 153 | 156 | 159 |
160 | 161 | 162 |
163 | Singer 164 | 167 | 168 | {% for singer in artists %} 169 | 172 | {% endfor %} 173 |
174 | 175 | 176 | 179 |
180 |
181 | 182 | 183 |
184 | {% for file in files %} 185 |
186 |
{{ loop.index }}.
187 |
{{ file }}
188 |
189 | 193 |
194 | 195 |
196 |
197 |
198 | {% endfor %} 199 |
200 |
201 | 202 | 234 | 235 | 236 | 237 | --------------------------------------------------------------------------------