Prepare data for simple chord classification


In [2]:
%pylab inline
import numpy as np
import pandas as pd
import sys
import math
import os

sys.path.append('../../tools/music-processing-experiments')

from analysis import split_to_blocks
from files import load_wav
from time_intervals import block_labels
from spectrogram import create_window
from reassignment import chromagram


Populating the interactive namespace from numpy and matplotlib

In [3]:
song = 'The_Beatles/01_-_Please_Please_Me/08_-_Love_Me_Do'
data_dir = 'data/beatles/'
audio_file = data_dir + 'audio-cd/' + song + '.wav'
chord_file = data_dir  + 'chordlab/' + song + '.lab.pcs.tsv'
audio_file, chord_file


Out[3]:
('data/beatles/audio-cd/The_Beatles/01_-_Please_Please_Me/08_-_Love_Me_Do.wav',
 'data/beatles/chordlab/The_Beatles/01_-_Please_Please_Me/08_-_Love_Me_Do.lab.pcs.tsv')

Load audio


In [4]:
x, fs = load_wav(audio_file)

In [5]:
print('shape:', x.shape)
print('sampling rate:', fs, 'Hz')
print('number of samples:', len(x))
print('duration in audio:', len(x) / fs, 'sec')


shape: (6295716,)
sampling rate: 44100 Hz
number of samples: 6295716
duration in audio: 142.76 sec

Load chords


In [6]:
chords = pd.read_csv(chord_file, sep='\t')

In [7]:
chords.head()


Out[7]:
start end label root bass C Db D Eb E F Gb G Ab A Bb B
0 0.000000 0.465952 N 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 0.465952 2.103232 G 7 7 0 0 1 0 0 0 0 1 0 0 0 1
2 2.103232 3.705409 C 0 0 1 0 0 0 1 0 0 1 0 0 0 0
3 3.705409 5.272756 G 7 7 0 0 1 0 0 0 0 1 0 0 0 1
4 5.272756 6.886543 C 0 0 1 0 0 0 1 0 0 1 0 0 0 0

In [8]:
chords.shape


Out[8]:
(73, 17)

In [9]:
print('duration in chords:', chords['end'].iloc[-1])


duration in chords: 142.759184

In [10]:
target_cols = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B']

Split audio to blocks


In [11]:
block_size = 4096
hop_size = 2048

In [12]:
x_blocks, x_times = split_to_blocks(x, block_size, hop_size, fs)
x_blocks.shape


Out[12]:
(3075, 4096)

In [13]:
print('number of blocks:', len(x_blocks))
# start times for each block
print('last block starts at:', x_times[-1], 'sec')


number of blocks: 3075
last block starts at: 142.756281179 sec

Mapping of chords to blocks


In [14]:
def chords_to_blocks(chords, block_center_times):
    chord_ix = 0
    for t in block_center_times:
        yield chords.iloc[i][target_cols]

In [15]:
def time_to_samples(time):
    return np.round(time * fs)

chords['start_sample'] = time_to_samples(chords['start'])
chords['end_sample'] = time_to_samples(chords['end'])
df_blocks = pd.DataFrame({'start': time_to_samples(x_times).astype(np.int64)})
df_blocks['end'] = df_blocks['start'] + block_size

In [16]:
chords.head()


Out[16]:
start end label root bass C Db D Eb E F Gb G Ab A Bb B start_sample end_sample
0 0.000000 0.465952 N 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20548
1 0.465952 2.103232 G 7 7 0 0 1 0 0 0 0 1 0 0 0 1 20548 92753
2 2.103232 3.705409 C 0 0 1 0 0 0 1 0 0 1 0 0 0 0 92753 163409
3 3.705409 5.272756 G 7 7 0 0 1 0 0 0 0 1 0 0 0 1 163409 232529
4 5.272756 6.886543 C 0 0 1 0 0 0 1 0 0 1 0 0 0 0 232529 303697

In [17]:
df_blocks.head()


Out[17]:
start end
0 0 4096
1 2048 6144
2 4096 8192
3 6144 10240
4 8192 12288

In [34]:
pcs_cols = ['C','Db','D','Eb','E','F','Gb','G','Ab','A','Bb','B']
label_cols = ['label','root','bass'] + pcs_cols

In [50]:
label_dict = chords[label_cols].drop_duplicates().set_index('label')

In [51]:
label_dict


Out[51]:
root bass C Db D Eb E F Gb G Ab A Bb B
label
N 0 0 0 0 0 0 0 0 0 0 0 0 0 0
G 7 7 0 0 1 0 0 0 0 1 0 0 0 1
C 0 0 1 0 0 0 1 0 0 1 0 0 0 0
D 2 2 0 0 1 0 0 0 1 0 0 1 0 0

In [28]:
df_labels = chords[['start_sample', 'end_sample', 'label']].copy()
df_labels.rename(columns={'start_sample': 'start', 'end_sample': 'end'}, inplace=True)

In [30]:
df_labels.head()


Out[30]:
start end label
0 0 20548 N
1 20548 92753 G
2 92753 163409 C
3 163409 232529 G
4 232529 303697 C

In [22]:
%time df_labelled_blocks = block_labels(df_blocks, df_labels)


CPU times: user 509 ms, sys: 6.36 ms, total: 516 ms
Wall time: 531 ms

In [23]:
df_labelled_blocks


Out[23]:
start end label
0 0 4096 N
1 2048 6144 N
2 4096 8192 N
3 6144 10240 N
4 8192 12288 N
5 10240 14336 N
6 12288 16384 N
7 14336 18432 N
8 16384 20480 N
9 18432 22528 G
10 20480 24576 G
11 22528 26624 G
12 24576 28672 G
13 26624 30720 G
14 28672 32768 G
15 30720 34816 G
16 32768 36864 G
17 34816 38912 G
18 36864 40960 G
19 38912 43008 G
20 40960 45056 G
21 43008 47104 G
22 45056 49152 G
23 47104 51200 G
24 49152 53248 G
25 51200 55296 G
26 53248 57344 G
27 55296 59392 G
28 57344 61440 G
29 59392 63488 G
... ... ... ...
3045 6236160 6240256 N
3046 6238208 6242304 N
3047 6240256 6244352 N
3048 6242304 6246400 N
3049 6244352 6248448 N
3050 6246400 6250496 N
3051 6248448 6252544 N
3052 6250496 6254592 N
3053 6252544 6256640 N
3054 6254592 6258688 N
3055 6256640 6260736 N
3056 6258688 6262784 N
3057 6260736 6264832 N
3058 6262784 6266880 N
3059 6264832 6268928 N
3060 6266880 6270976 N
3061 6268928 6273024 N
3062 6270976 6275072 N
3063 6273024 6277120 N
3064 6275072 6279168 N
3065 6277120 6281216 N
3066 6279168 6283264 N
3067 6281216 6285312 N
3068 6283264 6287360 N
3069 6285312 6289408 N
3070 6287360 6291456 N
3071 6289408 6293504 N
3072 6291456 6295552 N
3073 6293504 6297600 N
3074 6295552 6299648 N

3075 rows × 3 columns


In [73]:
# df_block_pcs = pd.merge(df_labelled_blocks[['label']], label_dict, how='inner', left_on='label', right_index=True)
df_block_pcs = df_labelled_blocks[['label']].join(label_dict, on='label')[['label'] + pcs_cols]

In [74]:
df_block_pcs[:15]


Out[74]:
label C Db D Eb E F Gb G Ab A Bb B
0 N 0 0 0 0 0 0 0 0 0 0 0 0
1 N 0 0 0 0 0 0 0 0 0 0 0 0
2 N 0 0 0 0 0 0 0 0 0 0 0 0
3 N 0 0 0 0 0 0 0 0 0 0 0 0
4 N 0 0 0 0 0 0 0 0 0 0 0 0
5 N 0 0 0 0 0 0 0 0 0 0 0 0
6 N 0 0 0 0 0 0 0 0 0 0 0 0
7 N 0 0 0 0 0 0 0 0 0 0 0 0
8 N 0 0 0 0 0 0 0 0 0 0 0 0
9 G 0 0 1 0 0 0 0 1 0 0 0 1
10 G 0 0 1 0 0 0 0 1 0 0 0 1
11 G 0 0 1 0 0 0 0 1 0 0 0 1
12 G 0 0 1 0 0 0 0 1 0 0 0 1
13 G 0 0 1 0 0 0 0 1 0 0 0 1
14 G 0 0 1 0 0 0 0 1 0 0 0 1

In [66]:
assert len(df_block_pcs) == len(df_blocks)

In [75]:
block_labels_file = data_dir + 'chord-pcs/4096_2048/' + song + '.pcs'
print(block_labels_file)
os.makedirs(os.path.dirname(block_labels_file), exist_ok=True)
df_block_pcs.to_csv(block_labels_file, sep='\t', index=False)


data/beatles/chord-pcs/4096_2048/The_Beatles/01_-_Please_Please_Me/08_-_Love_Me_Do.pcs

Chromagram features


In [26]:
w = create_window(block_size)
X_chromagram = chromagram(x_blocks, w, fs, to_log=True, bin_range=(-48, 67), bin_division=1)

In [27]:
chromagram_file = data_dir + 'chromagram/block=4096_hop=2048_bins=-48,67_div=1/' + song + '.npz'
print(chromagram_file)
os.makedirs(os.path.dirname(chromagram_file), exist_ok=True)
np.savez_compressed(chromagram_file, X=X_chromagram, times=x_times)


data/beatles/chromagram/block=4096_hop=2048_bins=-48,67_div=1/The_Beatles/01_-_Please_Please_Me/08_-_Love_Me_Do.npz