Machine Learning - WMAI010-05
Classifying keys in a piece of music using a Hidden Markov Model trained using Spotify data. For a Notebook containing the results, see results notebook.
from IPython.display import Video
Video("example/39C5FuZ8C8M0QI8CrMsPkR.mp4")
Let's visualize our data: the Chroma vectors obtained from the Spotify API. We'll take 1 sample to begin with.
Import packages
# local imports
from tracklist import TrackList
from data import load_analysis
# data science
import pandas as pd
from scipy import stats
import numpy as np
from scipy.stats import entropy
# plotting
import seaborn as sns
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt
import matplotlib.pylab as pylab
# notebook
from IPython.display import HTML
import IPython
from IPython.display import set_matplotlib_formats
%matplotlib inline
set_matplotlib_formats('svg')
params = {'legend.fontsize': 'x-large',
'figure.figsize': (13, 5),
'axes.labelsize': 'x-large',
'axes.titlesize':'x-large',
'xtick.labelsize':'x-large',
'ytick.labelsize':'x-large'}
pylab.rcParams.update(params)
Define variable
pitch_classes = {
0: 'C (also B♯, D#)',
1: 'C♯, D♭ (also B#)',
2: 'D (also C#, E#)',
3: 'D♯, E♭ (also F#)',
4: 'E (also D#, F♭)',
5: 'F (also E♯, G#)',
6: 'F♯, G♭ (also E#)',
7: 'G (also F#, A#)',
8: 'G♯, A♭',
9: 'A (also G#, B#)',
10: 'A♯, B♭ (also C#)',
11: 'B (also A#, C♭)'
}
data_dir = '../dataset'
song = 0
trim_length = 20
Load data
meta = TrackList.load_from_dir(data_dir)
all_tracks = meta.get_track_ids()
track_df = pd.read_csv('example/39C5FuZ8C8M0QI8CrMsPkR.txt')
max_pitch = track_df.groupby('start')['pitch'].transform(max) == track_df['pitch']
linedf = track_df[max_pitch]
sns.scatterplot(x='start', y='chroma', size='pitch',
data=track_df[track_df['start'] < 5], hue='chroma', legend=False)
plt.plot(linedf[linedf['start'] < 5].start, linedf[linedf['start'] < 5].chroma, color="black")
plt.xlabel('Time (in seconds)')
plt.ylabel('Key')
plt.title('Chroma vectors of first 5 seconds of \'Foreplay / Long Time\' track.')
plt.show()
def create_frame(frame, ax): # arguments are `frame number` and `fargs`
ax.cla()
window = 5 # show a window of 5 seconds
step = frame/20 # advance 0.05 seconds per frame
x1 = -window/2 + step
x2 = window/2 + step
scattersamples = (track_df['start'] >= x1) & (track_df['start'] <= x2)
sns.scatterplot(x='start', y='chroma', size='pitch',
data=track_df[scattersamples], ax=ax, hue='chroma', legend=False)
linesamples = (linedf['start'] >= x1) & (linedf['start'] <= x2)
plt.plot(linedf[linesamples].start, linedf[linesamples].chroma, color="black")
ax.set_xlim(x1, x2)
line1, = ax.plot(
[x1+window/2, x1+window/2],
['C (also B♯, D#)', 'B (also A#, C♭)'],
'--', linewidth=2, label='Dashes set retroactively')
plt.xlabel('Time (in seconds)')
plt.ylabel('Key')
plt.title('Chroma vectors of first 5 seconds of \'Foreplay / Long Time\' track.')
fig = plt.figure()
ax = fig.gca()
create_frame(0, ax)
fig = plt.figure()
ax = fig.gca()
animation = FuncAnimation(fig, create_frame, frames=300, fargs=(ax,),
interval=50) # Interval at 1000/50 = 20 frames per second
HTML(animation.to_jshtml())
Animation size has reached 20984501 bytes, exceeding the limit of 20971520.0. If you're sure you want a larger animation embedded, set the animation.embed_limit rc parameter to a larger value (in MB). This and further frames will be dropped.