Code
# A male and a female LibriSpeech reader (docs/speech/SOURCES.md), at 16 kHz.
READERS = {"Male reader": "3000_15664", "Female reader": "1462_170142"}
def without_pauses(snd, window=0.03, below_db=35, min_windows=8):
"""``snd`` with every run of at least ``min_windows`` windows more than ``below_db`` below the
loudest window cut out, the rest joined by ``so.splice`` with 10 ms cross-fades."""
n = int(round(window * snd.fs))
frames = snd.data[: len(snd) // n * n, 0].reshape(-1, n)
level = 10 * np.log10(np.mean(frames**2, axis=1) + 1e-20) # dB per window
quiet = np.concatenate([[False], level < level.max() - below_db, [False]])
edges = np.flatnonzero(np.diff(quiet.astype(int))) # starts and ends of quiet runs
keep = np.ones(len(frames), dtype=bool)
for start, end in zip(edges[::2], edges[1::2], strict=True):
if end - start >= min_windows:
keep[start:end] = False
return so.splice(snd, np.flatnonzero(keep) * n / snd.fs, n / snd.fs, crossfade=0.01, max_shift=0)
passages = {
label: without_pauses(so.load(fetch(f"docs/speech/librispeech_{reader}.flac")))
for label, reader in READERS.items()
}
for label, passage in passages.items():
print(f"{label}: {passage.duration:.1f} s without pauses")
SEGMENTS = [0.03, 0.12, 0.48, 0.96] # s
QUILT_DUR = 6.0 # s
quilt_bank = so.cosine_filterbank(30, 20, 8000) # the paper's bank, up to Nyquist at 16 kHz
def quilt_demo(passage):
"""The passage's first 6 s and a 6 s quilt at each segment length, with their cochleagrams
on one color scale."""
sounds = {"Original": so.Sound(passage.data[: int(QUILT_DUR * passage.fs)], passage.fs)}
for seg in SEGMENTS:
sounds[f"{seg * 1000:.0f} ms"] = so.quilt(passage, seg, QUILT_DUR, rng=1)
sounds = finish_together(sounds)
fig, axes = plt.subplots(len(sounds), 1, figsize=(7, 5), sharex=True, layout="constrained")
envelopes = {label: quilt_bank.analyze(snd).envelopes() for label, snd in sounds.items()}
top = envelopes["Original"].db[:, 1:-1].max() # all on the original's color scale
for ax, (label, env) in zip(axes, envelopes.items(), strict=True):
env.plot(ax, colorbar=False, db_max=top)
title = label if label == "Original" else f"Quilt, {label} segments"
ax.set(title=title, xlabel="", ylabel="", xlim=(0, QUILT_DUR))
axes[-1].set_xlabel("Time [s]")
fig.supylabel("Frequency [Hz]", fontsize=9)
return fig, sounds, {label: [ax] for label, ax in zip(sounds, axes, strict=True)}