Source code for pytta.utils.freq

"""

This utility provides frequency and fractional octave frequency bands functionalities.

For syntax purposes you should start with:
	
	>>> from pytta import utils as utils

Available functions:
	
	>>> utils.freq_to_band(freq, nthOct, ref, base)
	>>> utils.fractional_octave_frequencies(nthOct = 3, freqRange = (20., 20000.), refFreq = 1000., base = 10)
	>>> utils.normalize_frequencies(freqs, samplingRate = 44100)
	>>> utils.freqs_to_center_and_edges(freqs)
	>>> utils.filter_values(freq, values, nthOct = 3)

For further information, check the docstrings for each function 
mentioned above.

Authors:
	João Vitor G. Paes joao.paes@eac.ufsm.br and
	Caroline Gaudeoso caroline.gaudeoso@eac.ufsm.br
	Rinaldi Petrolli rinaldi.petrolli@eac.ufsm.br

"""

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.") 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)) nominal_frequencies = np.copy(__nominal_frequencies) if nthOct > 3: for i in range(1, int(nthOct / 3)): extra_nominal_frequencies = (nominal_frequencies[1:] + nominal_frequencies[:-1]) / 2 nominal_frequencies = np.concatenate((nominal_frequencies, extra_nominal_frequencies)) nominal_frequencies.sort(kind='mergesort') 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
def filter_values(freq, values, nthOct: int = 3): """ Filters the given values into nthOct bands. Parameters ---------- freq : ndarray Array containing the frequency axis. values : ndarray Array containing the magnitude values to be filtered. nthOct : int, optional Fractional octave bands that the absorption will be filtered to. Returns ------- bands : ndarray An array containing the center frequencies of the available bands. result : ndarray An array containing the filtered values in the available bands. """ bands = fractional_octave_frequencies(freqRange=(min(freq), max(freq)), nthOct=nthOct) # bands = bands[np.argwhere((bands[:, 1] >= min(freq)) & (bands[:, 1] <= max(freq)))[:, 0]] idx = np.array([np.argwhere((freq >= bands[a, 0]) & (freq <= bands[a, 2])) for a in np.arange(0, len(bands))], dtype=object) result = np.array([np.sum(values[idx[a]]) / len(idx[a]) for a in np.arange(0, len(bands))], dtype=object) result = np.nan_to_num(result) return bands[:, 1], result.astype(values.dtype) # ref: one_third_octave - Copyleft 2007-2011 luc.jaouen@matelys.com