Ajout script pour décoder les notes

This commit is contained in:
Mattéo Delabre 2020-12-19 21:39:41 +01:00
parent a9cd4d2207
commit e751df5f8b
Signed by: matteo
GPG Key ID: AE3FBD02DC583ABB
4 changed files with 73 additions and 16 deletions

33
fft-decode.py Normal file
View File

@ -0,0 +1,33 @@
import soundbox
import numpy as np
import scipy.signal as sig
import sys
if len(sys.argv) != 2:
print(f"""Utilisation: {sys.argv[0]} [source]
Détermine les notes jouées dans le fichier [source] en utilisant
la transformation de Fourier.""")
sys.exit(1)
source_file = sys.argv[1]
# Calcul du FFT
signal = soundbox.load_signal(source_file)
freq_scale = soundbox.samp_rate / len(signal)
vecs = np.fft.fft(signal)[:soundbox.samp_rate // 2]
values = np.absolute(vecs) / np.max(np.absolute(vecs))
freq = np.arange(len(values))
# Calcul de la fenêtre des fréquences intéressantes
high_enough = np.where(values >= 0.005)
left = high_enough[0][0]
right = high_enough[0][-1]
freq = freq[left:right]
values = values[left:right]
# Recherche des pics
peaks, _ = sig.find_peaks(values, height=0.1, distance=10 / freq_scale)
print(list(map(soundbox.freq_note, freq[peaks] * freq_scale)))

View File

@ -17,8 +17,19 @@ output_file = sys.argv[2]
# Calcul du FFT # Calcul du FFT
signal = soundbox.load_signal(source_file) signal = soundbox.load_signal(source_file)
freq_scale = soundbox.samp_rate / len(signal)
vecs = np.fft.fft(signal)[:soundbox.samp_rate // 2] vecs = np.fft.fft(signal)[:soundbox.samp_rate // 2]
values = np.absolute(vecs) / np.max(np.absolute(vecs)) values = np.absolute(vecs) / np.max(np.absolute(vecs))
freq = np.arange(len(values))
# Calcul de la fenêtre des fréquences intéressantes
high_enough = np.where(values >= 0.005)
left = high_enough[0][0]
right = high_enough[0][-1]
freq = freq[left:right]
values = values[left:right]
# Génération du graphe # Génération du graphe
plt.style.use('ggplot') plt.style.use('ggplot')
@ -30,15 +41,9 @@ plt.rcParams.update({
fig, ax = plt.subplots() fig, ax = plt.subplots()
ax.tick_params(axis='both', which='major', labelsize=12) ax.tick_params(axis='both', which='major', labelsize=12)
ax.plot(freq, values)
freq_filter = values >= 0.01
freq = np.arange(len(values))
ax.plot(freq[freq_filter], values[freq_filter])
# Configuration des axes # Configuration des axes
freq_scale = soundbox.samp_rate / len(signal)
def freq_format(value, pos): def freq_format(value, pos):
return f'{value * freq_scale:.0f} Hz' return f'{value * freq_scale:.0f} Hz'

View File

@ -113,20 +113,21 @@ def chord(instr, dur, freqs, value=1):
return signal return signal
notes = { notes = {
'do': 0, 'si#': 0, 'si#': 0, 'do': 0,
'do#': 1, 'reb': 1, 'reb': 1, 'do#': 1,
're': 2, 're': 2,
're#': 3, 'mib': 3, 'mib': 3, 're#': 3,
'mi': 4, 'fab': 4, 'fab': 4, 'mi': 4,
'fa': 5, 'mi#': 5, 'mi#': 5, 'fa': 5,
'fa#': 6, 'solb': 6, 'solb': 6, 'fa#': 6,
'sol': 7, 'sol': 7,
'sol#': 8, 'lab': 8, 'lab': 8, 'sol#': 8,
'la': 9, 'la': 9,
'la#': 10, 'sib': 10, 'sib': 10, 'la#': 10,
'si': 11, 'dob': 11, 'dob': 11, 'si': 11,
} }
rev_notes = dict(zip(*reversed(list(zip(*notes.items())))))
def note_freq(note, octave): def note_freq(note, octave):
""" """
@ -144,6 +145,24 @@ def note_freq(note, octave):
* math.pow(2, (notes[note] - 9) / 12)) * math.pow(2, (notes[note] - 9) / 12))
def freq_note(freq):
"""
Retrouve la note correspondant au mieux à une fréquence.
Paramètres:
note
"""
log = math.log2(freq / note_freq('do', 3))
octave = math.floor(log) + 3
note = round(12 * log - 12 * math.floor(log))
if note == 12:
octave += 1
note = 0
return (rev_notes[note], octave)
def note_freqs(notes): def note_freqs(notes):
"""Calcule la fréquence correspondant à un ensemble de notes.""" """Calcule la fréquence correspondant à un ensemble de notes."""
return list(map(lambda info: note_freq(*info), notes)) return list(map(lambda info: note_freq(*info), notes))

Binary file not shown.