# -*- coding: utf-8 -*-
"""
Provides frequency and fractional octave frequency bands functionalities.
@author: João Vitor Gutkoski Paes
"""
import numpy as np
from typing import Tuple
# Cálculo de bandas de oitava a partir de 1 kHz utilizando base 10 ou 2
__nominal_frequencies = np.array([
0.1, 0.125, 0.16, 0.2, 0.25, 0.315, 0.4, 0.5, 0.6, 3., 0.8,
1., 1.25, 1.6, 2., 2.5, 3.15, 4., 5., 6.3, 8., 10., 12.5, 16.,
20., 25., 31.5, 40., 50., 63., 80., 100., 125., 160., 200., 250.,
315., 400., 500., 630., 800., 1000., 1250., 1600., 2000., 2500.,
3150., 4000., 5000., 6300., 8000., 10000., 12500., 16000., 20000.
])
[docs]def freq_to_band(freq: float, nthOct: int, ref: float, base: int) -> int:
"""
Band number from frequency value.
Parameters
----------
freq : float
The frequency value.
nthOct : int
How many bands per octave.
ref : float
Frequency of reference, or band number 0.
base : int
Either 10 or 2.
Raises
------
ValueError
If base is not 10 nor 2 raises value error.
Returns
-------
int
The band number from center.
"""
if base == 10:
log = np.log10
factor = 3 / 10
elif base == 2:
log = np.log2
factor = 1
else:
raise ValueError(f"freq_to_band: unknown base value: {base}.")
return int(np.round(log(freq / ref) * (nthOct / factor)))
[docs]def fractional_octave_frequencies(nthOct: int = 3,
freqRange: Tuple[float] = (20., 20000.),
refFreq: float = 1000.,
base: int = 10) -> np.ndarray:
"""
Lower, center and upper frequency values of all bands within range.
Parameters
----------
nthOct : int, optional
bands of octave/nthOct. The default is 3.
freqRange : Tuple[float], optional
frequency range. These frequencies are inside the lower and higher band, respectively.
The default is (20., 20000.).
refFreq : float, optional
Center frequency of center band. The default is 1000..
base : int, optional
Either 10 or 2. The default is 10.
Returns
-------
freqs : numpy.ndarray
Array with shape (N, 3).
"""
if base == 10:
factor = 3 / 10
elif base == 2:
factor = 1
minFreq, maxFreq = freqRange
minBand = freq_to_band(minFreq, nthOct, refFreq, base)
maxBand = freq_to_band(maxFreq, nthOct, refFreq, base)
bands = np.arange(minBand, maxBand + 1)
freqs = np.zeros((len(bands), 3))
nthOct = 1 / nthOct
for k, band in enumerate(bands):
dummy = refFreq * base ** (band * nthOct * factor)
dummy = np.sqrt((__nominal_frequencies - dummy) ** 2)
center = __nominal_frequencies[np.argmin(dummy)]
lower = center / base ** (nthOct * factor / 2)
upper = center * base ** (nthOct * factor / 2)
freqs[k, :] = [lower, center, upper]
return freqs
[docs]def normalize_frequencies(freqs: np.ndarray,
samplingRate: int = 44100) -> np.ndarray:
"""
Normalize frequencies for any sampling rate.
Parameters
----------
freqs : np.ndarray
DESCRIPTION.
samplingRate : int, optional
DESCRIPTION. The default is 44100.
Returns
-------
TYPE
DESCRIPTION.
"""
nyq = samplingRate // 2
return freqs / nyq
[docs]def freqs_to_center_and_edges(freqs: np.ndarray) -> Tuple[np.ndarray]:
"""
Separate the array returned from `fractional_octave_frequencies`.
The returned arrays corresponde to the center and edge frequencies of
the fractional octave bands
Parameters
----------
freqs : np.ndarray
Array returned from `fractional_octave_frequencies`.
Returns
-------
center : np.ndarray
Center frequencies of the bands.
edges : np.ndarray
Edge frequencies (lower and upper) of the bands.
"""
center = freqs[:, 1].T
edges = np.array([freqs[:, 0], freqs[:, 2]]).T
return center, edges