Example usage

[ ]:
import sys
!{ sys.executable } -m pip install biosonic
Requirement already satisfied: biosonic in /home/lena/.cache/pypoetry/virtualenvs/biosonic-WfDzQ51_-py3.14/lib/python3.14/site-packages (0.1.8)
Requirement already satisfied: numpy<3.0,>=2.4.0 in /home/lena/.cache/pypoetry/virtualenvs/biosonic-WfDzQ51_-py3.14/lib/python3.14/site-packages (from biosonic) (2.5.2)
Requirement already satisfied: pandas<3.0.0,>=2.3.3 in /home/lena/.cache/pypoetry/virtualenvs/biosonic-WfDzQ51_-py3.14/lib/python3.14/site-packages (from biosonic) (2.3.3)
Requirement already satisfied: scipy<2.0.0,>=1.16.3 in /home/lena/.cache/pypoetry/virtualenvs/biosonic-WfDzQ51_-py3.14/lib/python3.14/site-packages (from biosonic) (1.18.0)
Requirement already satisfied: python-dateutil>=2.8.2 in /home/lena/.cache/pypoetry/virtualenvs/biosonic-WfDzQ51_-py3.14/lib/python3.14/site-packages (from pandas<3.0.0,>=2.3.3->biosonic) (2.9.0.post0)
Requirement already satisfied: pytz>=2020.1 in /home/lena/.cache/pypoetry/virtualenvs/biosonic-WfDzQ51_-py3.14/lib/python3.14/site-packages (from pandas<3.0.0,>=2.3.3->biosonic) (2026.3.post1)
Requirement already satisfied: tzdata>=2022.7 in /home/lena/.cache/pypoetry/virtualenvs/biosonic-WfDzQ51_-py3.14/lib/python3.14/site-packages (from pandas<3.0.0,>=2.3.3->biosonic) (2026.3)
Requirement already satisfied: six>=1.5 in /home/lena/.cache/pypoetry/virtualenvs/biosonic-WfDzQ51_-py3.14/lib/python3.14/site-packages (from python-dateutil>=2.8.2->pandas<3.0.0,>=2.3.3->biosonic) (1.17.0)
[2]:
from biosonic import handle, plot, compute, filter

Read file and plot spectrogram

[3]:
data_folder = "./example_data/"
[4]:
audioSignal = handle.read_wav(data_folder+"GT00024_G00219_Dagobert_distance.wav")
print(f"sampling rate: {audioSignal.srate}, number of channels: {audioSignal.numchannels}, quantization: {audioSignal.quantization}")
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[4], line 2
      1 audioSignal = handle.read_wav(data_folder+"GT00024_G00219_Dagobert_distance.wav")
----> 2 print(f"sampling rate: {audioSignal.srate}, number of channels: {audioSignal.numchannels}, quantization: {audioSignal.quantization}")

AttributeError: 'tuple' object has no attribute 'srate'
[ ]:
WINDOW_LENGTH = 512
DYNAMIC_RANGE = 70

plot.plot_spectrogram(audioSignal, window_length=WINDOW_LENGTH, overlap=95, dynamic_range=DYNAMIC_RANGE)
[ ]:
import numpy as np
import matplotlib.pyplot as plt

hz = np.linspace(0, 22000, 1000)
mel= compute.utils.hz_to_mel(hz, corner_frequency=1000)
plt.plot(hz, mel, label="1000")
mel= compute.utils.hz_to_mel(hz, corner_frequency=500)
plt.plot(hz, mel, label="500")
mel= compute.utils.hz_to_mel(hz, corner_frequency=3000)
plt.plot(hz, mel, label="3000")

plt.legend()
plt.xlabel("Frequency (Hz)")
plt.ylabel("Mel")
plt.show()
[ ]:
plot.plot_spectrogram(audioSignal, window_length=WINDOW_LENGTH, overlap=95, dynamic_range=DYNAMIC_RANGE, freq_scale="mel", n_bands=40)
plot.plot_spectrogram(audioSignal, window_length=WINDOW_LENGTH, overlap=95, dynamic_range=DYNAMIC_RANGE, freq_scale="mel", n_bands=128, corner_frequency=5000)
[ ]:
# import librosa

# S = librosa.feature.melspectrogram(y=data, sr=sr, n_fft=WINDOW_LENGTH, n_mels=128)
# fig, ax = plt.subplots()
# S_dB = librosa.power_to_db(S, ref=np.max)
# img = librosa.display.specshow(S_dB, x_axis='time',
#                          y_axis='mel', sr=sr, ax=ax, cmap="binary")
# fig.colorbar(img, ax=ax, format='%+2.0f dB')
# ax.set(title='Mel-frequency spectrogram')
[ ]:
plot.plot_spectrogram(audioSignal, window_length=WINDOW_LENGTH, overlap=95, dynamic_range=DYNAMIC_RANGE, freq_scale="log")

Cepstrum and cepstral coefficients

[ ]:
ceps = compute.spectrotemporal.cepstrum(audioSignal)
plot.plot_cepstrum(ceps, audioSignal.srate, max_quefrency=1/500, min_quefrency=1/5000)
[ ]:
plot.plot_cepstral_coefficients(audioSignal, audioSignal.srate, WINDOW_LENGTH, filterbank_type="log", n_ceps=18, fmin=2000)
[ ]:
plot.plot_cepstral_coefficients(audioSignal, audioSignal.srate, WINDOW_LENGTH, filterbank_type="linear", n_ceps=18, fmin=2000)
[ ]:
plot.plot_cepstral_coefficients(audioSignal, WINDOW_LENGTH, filterbank_type="mel", n_ceps=18, fmin=2000)
[ ]:
from scipy.signal import sawtooth
perios_s = 1
times = np.linspace(0, perios_s, perios_s * 44100)
f_Hz = 50
# sine_w = np.sin(2 * np.pi * f_Hz * times) + np.sin(2 * np.pi * f_Hz*2 * times)

saw = sawtooth(2 * np.pi * f_Hz * times)
# plt.plot(times, sine_w)
# plt.show()
plot.plot_cepstrum(saw, audioSignal.srate, min_quefrency=1/500, max_quefrency=1/30)

Filter signal

[ ]:
audioSignal = handle.read_wav(data_folder+"/201.wav")
print(f"sampling rate: {audioSignal.srate}, number of channels: {audioSignal.numchannels}, quantization: {audioSignal.quantization}")
plot.plot_spectrogram(audioSignal, dynamic_range=DYNAMIC_RANGE)
[ ]:
x_filtered = filter.filter(audioSignal, f_cutoff=2500, type="highpass")
plot.plot_spectrogram(x_filtered, audioSignal.srate, dynamic_range=DYNAMIC_RANGE)
[ ]:
# change order for steeper frequency cutoff
x_filtered = filter.filter(x_filtered, audioSignal.srate, f_cutoff=17500, type="lowpass", order=4)
plot.plot_spectrogram(x_filtered, audioSignal.srate, dynamic_range=DYNAMIC_RANGE)

Fundamental frequency tracking

[ ]:
# praat autocorrelation fundamental frequency tracking
time_points, candidates ,_ ,_= compute.fundamental.boersma(x_filtered, audioSignal.srate, min_f0=2000, max_f0=6000, voicing_thresh=.3, timestep=0.02, octave_cost=0.03, silence_thresh=0.05, plot=True, window_length=WINDOW_LENGTH, overlap=95, flim=(0,17000), dynamic_range=DYNAMIC_RANGE)

Audio feature extraction

[ ]:
features = compute.utils.extract_all_features(x_filtered, audioSignal.srate, min_prominence=0.7, noise_threshold=0.5, plot=True, envelope_kwargs={"silence_threshold": 0.05}, spec_kwargs={"dynamic_range": DYNAMIC_RANGE}) # lower resolution for dom freqs?
[ ]:
x2, sr2, _, _ = handle.read_wav(data_folder+"GT00024_G00219_Dagobert_distance.wav")
features = compute.utils.extract_all_features(x2, sr2, min_prominence=0.7, noise_threshold=0.5, plot=True, envelope_kwargs={"silence_threshold": 0.01}, spec_kwargs={"dynamic_range": DYNAMIC_RANGE}) # lower resolution for dom freqs?
[ ]:
features
[ ]:
features = compute.utils.extract_all_features(
    x_filtered,
    audioSignal.srate,
    min_prominence=0.7, # for dominant frequencies
    noise_threshold=0.3, # for dominant frequencies
    plot=True,
    spec_kwargs={"dynamic_range": DYNAMIC_RANGE},
    envelope_kwargs={"silence_threshold": 0.02}
    )

Batch normalize files in a folder and export features as csv

[ ]:
handle.batch_normalize_wav_files(data_folder, 44100, 1, "float32")
[ ]:
features_df = handle.batch_extract_features(data_folder+"/normalized", save_csv_path="extracted_features.csv")
[ ]:
features_df

Parse praat TextGrids and extract segments from file

This relies on the praat-textgrids library written by Tommi Nieminen:

https://github.com/Legisign/Praat-textgrids

[ ]:
audioSignal = handle.read_wav(data_folder+"/201.wav", n_channels=1)
[ ]:
# get boundaries and plot
segments = handle.boundaries_from_textgrid(data_folder+"/201.TextGrid", tier_name="segments")
[ ]:
segments
[ ]:
plot.plot_boundaries_on_spectrogram(audioSignal, segments, dynamic_range=DYNAMIC_RANGE)
[ ]:
# or extract signal segments directly
audio_segments = handle.audio_segments_from_textgrid(audioSignal, data_folder+"/201.TextGrid", tier_name="segments", dynamic_range=DYNAMIC_RANGE)
[ ]:
audio_segments
[ ]:
plot.plot_spectrogram_catalogue(audio_segments, "label", ncols=4, dynamic_range=DYNAMIC_RANGE)
[ ]:
audio_segments['spectrogram'] = audio_segments.apply(
    lambda row: compute.utils.transform_spectrogram_for_nn(
        data=row['waveform'],
        sr=row['sr'],
        values_type='float32',
        f_min = 1500,
        f_max = 15000,
        window_length = WINDOW_LENGTH,
        resize=(128, 128)
    ),
    axis=1
)
audio_segments.head()
[ ]:
n_cols = 3
n_rows = (len(audio_segments) + n_cols - 1) // n_cols

fig, axes = plt.subplots(nrows=n_rows, ncols=n_cols, figsize=(n_cols*3, n_rows*3))
axes = axes.flatten()

for i, (ax, spec) in enumerate(zip(axes, audio_segments['spectrogram'])):
    if spec.ndim == 3:
        spec_to_plot = spec[0]
    else:
        spec_to_plot = spec

    ax.imshow(spec_to_plot, origin='lower', aspect='auto', cmap='binary')
    ax.axis("off")
    ax.set_title(f"{audio_segments.iloc[i]['label']} - {audio_segments.iloc[i]['filename']}", fontsize=10)

for ax in axes[len(audio_segments):]:
    ax.axis("off")

plt.tight_layout()
plt.show()

Read all files in a folder into DataFrame and plot spectrogram catalogue

[ ]:
df = handle.batch_read_files_to_df(data_folder+"/normalized")
df.head()
[ ]:
plot.plot_spectrogram_catalogue(df, "waveform", ncols=5, per_page=25, title_columns=["filename", "sr"], dynamic_range=55)

Future

  • Tokuda NLM

  • Yin pitch tracking + Pitch tracking wrapper

  • modulation spectra

  • event detection/segmentation

  • different noise reduction approaches

  • dt(f)w

  • autocorrelation + crosscorr

  • spectral flux