audio-reactive-led-strip/python/melbank.py
not-matt 5e68b99b28
Add files via upload
New effect: Bars
Better frequency scaling by modifying parameters in melbank.py
Mel graph [before](https://imgur.com/a/xN9PA), [after](https://imgur.com/a/YYGLk)
This stretches out the lower end with vocals and bass, and squishes up the high end which usually takes up moer space on the strip for similar "sounds". Now it looks more like you would expect it to, based on what you hear (more pitch-like than frequency)
2017-12-27 16:28:13 +00:00

155 lines
4.9 KiB
Python

"""This module implements a Mel Filter Bank.
In other words it is a filter bank with triangular shaped bands
arnged on the mel frequency scale.
An example ist shown in the following figure:
.. plot::
from pylab import plt
import melbank
f1, f2 = 1000, 8000
melmat, (melfreq, fftfreq) = melbank.compute_melmat(6, f1, f2, num_fft_bands=4097)
fig, ax = plt.subplots(figsize=(8, 3))
ax.plot(fftfreq, melmat.T)
ax.grid(True)
ax.set_ylabel('Weight')
ax.set_xlabel('Frequency / Hz')
ax.set_xlim((f1, f2))
ax2 = ax.twiny()
ax2.xaxis.set_ticks_position('top')
ax2.set_xlim((f1, f2))
ax2.xaxis.set_ticks(melbank.mel_to_hertz(melfreq))
ax2.xaxis.set_ticklabels(['{:.0f}'.format(mf) for mf in melfreq])
ax2.set_xlabel('Frequency / mel')
plt.tight_layout()
fig, ax = plt.subplots()
ax.matshow(melmat)
plt.axis('equal')
plt.axis('tight')
plt.title('Mel Matrix')
plt.tight_layout()
Functions
---------
"""
from numpy import abs, append, arange, insert, linspace, log10, round, zeros
from math import log
def hertz_to_mel(freq):
"""Returns mel-frequency from linear frequency input.
Parameter
---------
freq : scalar or ndarray
Frequency value or array in Hz.
Returns
-------
mel : scalar or ndarray
Mel-frequency value or ndarray in Mel
"""
#return 2595.0 * log10(1 + (freq / 700.0))
return 3340.0 * log(1 + (freq / 250.0), 9)
def mel_to_hertz(mel):
"""Returns frequency from mel-frequency input.
Parameter
---------
mel : scalar or ndarray
Mel-frequency value or ndarray in Mel
Returns
-------
freq : scalar or ndarray
Frequency value or array in Hz.
"""
#return 700.0 * (10**(mel / 2595.0)) - 700.0
return 250.0 * (9**(mel / 3340.0)) - 250.0
def melfrequencies_mel_filterbank(num_bands, freq_min, freq_max, num_fft_bands):
"""Returns centerfrequencies and band edges for a mel filter bank
Parameters
----------
num_bands : int
Number of mel bands.
freq_min : scalar
Minimum frequency for the first band.
freq_max : scalar
Maximum frequency for the last band.
num_fft_bands : int
Number of fft bands.
Returns
-------
center_frequencies_mel : ndarray
lower_edges_mel : ndarray
upper_edges_mel : ndarray
"""
mel_max = hertz_to_mel(freq_max)
mel_min = hertz_to_mel(freq_min)
delta_mel = abs(mel_max - mel_min) / (num_bands + 1.0)
frequencies_mel = mel_min + delta_mel * arange(0, num_bands + 2)
lower_edges_mel = frequencies_mel[:-2]
upper_edges_mel = frequencies_mel[2:]
center_frequencies_mel = frequencies_mel[1:-1]
return center_frequencies_mel, lower_edges_mel, upper_edges_mel
def compute_melmat(num_mel_bands=12, freq_min=64, freq_max=8000,
num_fft_bands=513, sample_rate=16000):
"""Returns tranformation matrix for mel spectrum.
Parameters
----------
num_mel_bands : int
Number of mel bands. Number of rows in melmat.
Default: 24
freq_min : scalar
Minimum frequency for the first band.
Default: 64
freq_max : scalar
Maximum frequency for the last band.
Default: 8000
num_fft_bands : int
Number of fft-frequenc bands. This ist NFFT/2+1 !
number of columns in melmat.
Default: 513 (this means NFFT=1024)
sample_rate : scalar
Sample rate for the signals that will be used.
Default: 44100
Returns
-------
melmat : ndarray
Transformation matrix for the mel spectrum.
Use this with fft spectra of num_fft_bands_bands length
and multiply the spectrum with the melmat
this will tranform your fft-spectrum
to a mel-spectrum.
frequencies : tuple (ndarray <num_mel_bands>, ndarray <num_fft_bands>)
Center frequencies of the mel bands, center frequencies of fft spectrum.
"""
center_frequencies_mel, lower_edges_mel, upper_edges_mel = \
melfrequencies_mel_filterbank(
num_mel_bands,
freq_min,
freq_max,
num_fft_bands
)
center_frequencies_hz = mel_to_hertz(center_frequencies_mel)
lower_edges_hz = mel_to_hertz(lower_edges_mel)
upper_edges_hz = mel_to_hertz(upper_edges_mel)
freqs = linspace(0.0, sample_rate / 2.0, num_fft_bands)
melmat = zeros((num_mel_bands, num_fft_bands))
for imelband, (center, lower, upper) in enumerate(zip(
center_frequencies_hz, lower_edges_hz, upper_edges_hz)):
left_slope = (freqs >= lower) == (freqs <= center)
melmat[imelband, left_slope] = (
(freqs[left_slope] - lower) / (center - lower)
)
right_slope = (freqs >= center) == (freqs <= upper)
melmat[imelband, right_slope] = (
(upper - freqs[right_slope]) / (upper - center)
)
return melmat, (center_frequencies_mel, freqs)