-
Notifications
You must be signed in to change notification settings - Fork 0
/
estimate_notes.py
94 lines (66 loc) · 2.85 KB
/
estimate_notes.py
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
import numpy as np
import essentia.standard as es
import pickle
def transpose_notes(notes, octave=0):
""" Transpose Notes to an Octave Above 60 """
if min(notes) >= 60:
return notes
notes = [int(note) + 12 for note in notes]
octave += 1
return transpose_notes(notes, octave)
def estimate_pitch_melodia(audiofile):
""" Estimate Pitch from Audio File """
loader = es.EqloudLoader(filename=audiofile, sampleRate=44100)
audio = loader()
pitch_extractor = es.PredominantPitchMelodia(frameSize=2048, hopSize=128)
pitch_values, _ = pitch_extractor(audio)
onsets, durations, notes = es.PitchContourSegmentation(hopSize=128)(pitch_values, audio)
return notes
def get_chord_from_audio(audiofile):
""" Get Chord from Audio File """
# Predict Notes
all_notes = set(estimate_pitch_melodia(audiofile))
# Transpose Notes so that Lowest Note is above 60
all_notes_transposed = sorted(transpose_notes(all_notes))
print(f'All Present Notes: {all_notes_transposed}')
# Find Closest Chord
chord_length = len(all_notes_transposed)
# Chord Limit to Triad
chord_lim = 3
# limit length to n notes
if chord_length > chord_lim:
chord_length = chord_lim
input_chord = all_notes_transposed[:chord_lim]
else:
input_chord = all_notes_transposed
chord_length = len(input_chord)
return input_chord, chord_length
def get_weighted_distance(chord1, chord2, weight):
"""Calculate weighted Euclidean distance between two chords"""
weighted_diff = (chord1[0] - chord2[0]) * weight
weighted_diff += (chord1[1] - chord2[1]) * weight
regular_diff = np.sum((np.array(chord1[2:]) - np.array(chord2[2:])))
return np.sqrt(weighted_diff + regular_diff)
def get_closest_chord(input_chord, unique_midi_chords, chord_length, weight=10.0):
"""Get closest chord from a list of unique chords, with a higher weighting on the first element"""
# Get chords with the same length
chords_same_length = [ch for ch in unique_midi_chords if len(ch) == chord_length]
# Find closest chord to input
min_distance = np.inf
closest_chord = None
for chord in chords_same_length:
distance = get_weighted_distance(input_chord, chord, weight)
if distance < min_distance:
min_distance = distance
closest_chord = chord
return closest_chord
if __name__ == "__main__":
# Testing Estimation
file_name = 'Loop1_ragtime'
audiofile = f"data/loops/{file_name}.aif"
# Load the dictionary from a file
with open('unique_midi_chords.pkl', 'rb') as f:
unique_midi_chords = pickle.load(f)
input_chord, chord_length = get_chord_from_audio(audiofile)
closest_chord = get_closest_chord(input_chord, unique_midi_chords, chord_length)
print(f'Input Chord: {input_chord} \n Most Similar Chord {closest_chord}')