Source code for vocalpy.spectrogram
"""Convenience function that generates a spectrogram."""
from __future__ import annotations
from typing import Mapping
import librosa
import numpy as np
from . import spectral
from ._spectrogram.data_type import Spectrogram
from .params import Params
from .sound import Sound
METHODS = [
"librosa-db",
"sat-multitaper",
"soundsig-spectro",
]
[docs]
def spectrogram(
sound: Sound,
n_fft: int = 512,
hop_length: int = 64,
method="librosa-db",
params: Mapping | Params | None = None,
) -> Spectrogram:
"""Get a spectrogram from audio.
This is a convenience function that takes an instance of :class:`vocalpy.Sound`
and returns an instance of :class:`vocalpy.Spectrogram`. The
:attr:`vocalpy.Spectrogram.data` will be a spectral representation
computed according to the specified `method`.
Methods
=======
* `'librosa-db`': dB-scaled spectrogram
Equivalent to calling ``S = librosa.STFT(sound.data)``
and then ``S = librosa.amplitude_to_db(np.abs(S))``.
* ``'sat-multitaper``: multi-taper spectrogram computed the same way
that the Sound Analysis Toolbox for Matlab (SAT) does.
* ``'soundsig-spectro``: dB-scaled spectrogram
computed with Gaussian window; replicates the result of
the method ``soundsig.BioSound.spectroCalc`` in the
``soundsig`` package.
Parameters
----------
sound : vocalpy.Sound
Audio used to compute spectrogram.
n_fft : int
Length of the frame used for the Fast Fourier Transform,
in number of audio samples. Default is 512.
hop_length : int
Number of audio samples to "hop" for each frame
whe computing the Fast Fourier Transform.
Smaller values increase the number of columns in the spectrogram,
without affecting the frequency resolution of the STFT.
method : str
Name of method.
Default is `'librosa-db'`.
params : vocalpy.Params, mapping, None
A dictionary-like mapping from function parameter names
to argument values.
Returns
-------
spect : vocalpy.Spectrogram
A :class:`vocalpy.Spectrogram` instance
computed according to `method`
"""
if not isinstance(sound, Sound):
raise TypeError(
f"audio must be an instance of `vocalpy.Sound` but was: {type(sound)}"
)
if method not in METHODS:
raise ValueError(
f"Invalid `method`: {method}.\n" f"Valid methods are: {METHODS}\n"
)
if method == "librosa-db":
S = librosa.stft(sound.data, n_fft=n_fft, hop_length=hop_length)
S = librosa.amplitude_to_db(np.abs(S))
t = librosa.frames_to_time(
frames=np.arange(S.shape[-1]),
sr=sound.samplerate,
hop_length=hop_length,
)
f = librosa.fft_frequencies(sr=sound.samplerate, n_fft=n_fft)
spect = Spectrogram(data=S, frequencies=f, times=t)
elif method == "sat-multitaper":
spect: Spectrogram = spectral.sat_multitaper(sound, n_fft, hop_length)
elif method == "soundsig-spectro":
spect: Spectrogram = spectral.soundsig_spectro(
sound, n_fft, hop_length, **params
)
else:
raise ValueError(f"Unknown method: {method}")
return spect