Signal processing
Compose filters, edits, spectra, and features with the ops module.
Filter and normalize
Filter cutoffs are in Hz and must lie between zero and half the sample rate (the Nyquist frequency). The Butterworth filters operate per channel using causal filtering; they are not zero-phase filters.
normalize() scales the global peak to peak_level. gain() uses decibels; scaling and mixing can exceed the normal audio range. Normalize or clip deliberately before export or playback.
from phonotensor import ops
signal = ops.sine(440, duration=2.0, amplitude=0.2)
noise = ops.white_noise(duration=2.0, amplitude=0.03, seed=7)
filtered = ops.lowpass(signal + noise, cutoff=2000, order=5)
result = ops.normalize(filtered, peak_level=0.8)
print(ops.rms(result), ops.db_peak(result))Trim, arrange, and combine
Time arguments are in seconds. concatenate() and overlay() require matching sample rates and channel counts. overlay() adds the layer to the base and extends the output if needed; use nonnegative offsets.
intro = ops.trim(result, start=0.0, end=0.5)
with_space = ops.pad(intro, before=0.1, after=0.2)
sequence = ops.concatenate(with_space, with_space)
layered = ops.overlay(sequence, intro, offset=0.3)Inspect the frequency content
stft() returns complex-valued frames. In this release fft() returns magnitudes, not complex coefficients. Spectral analysis first averages multichannel audio to mono.
STFT pads the tail but does not center-pad the start. ISTFT uses overlap-add; pass length to trim the reconstructed tail. Window endpoints can lose information, so do not assume an exact round trip at the boundaries.
spectrum = ops.stft(result, n_fft=2048, hop_length=512)
magnitude = ops.magnitude(spectrum)
phase = ops.phase(spectrum)
reconstructed = ops.istft(spectrum, length=result.num_samples)
frequencies = ops.frequency_axis(result.sample_rate, n_fft=2048)
assert magnitude.n_features == len(frequencies)Extract features for analysis
Mel spectrograms contain power values. MFCC uses a natural log and the library’s own cosine transform, starting at coefficient 1. Its values should not be assumed equivalent to other libraries’ MFCC defaults.
Spectral centroid, rolloff, flatness, and bandwidth return a FeatureTensor with one feature per frame. These operations also downmix input to mono.
mel = ops.mel_spectrogram(result, n_mels=64, n_fft=1024)
coefficients = ops.mfcc(result, n_mfcc=13, n_mels=64)
centroid = ops.spectral_centroid(result)
print(mel.shape, coefficients.shape, centroid.shape)Pitch and timing
time_stretch() takes a positive rate: values greater than one shorten the output. pitch_shift() takes semitones. Both functions average channels to mono internally and duplicate the result to the original channel count, so stereo separation is not preserved.
estimate_f0() uses autocorrelation over nonoverlapping frames with a search range of roughly 50–1000 Hz. It returns 0.0 for unvoiced or insufficiently correlated frames. Treat it as a basic estimator, not a speech recognition model.
faster = ops.time_stretch(result, rate=1.25)
higher = ops.pitch_shift(result, semitones=3)
f0 = ops.estimate_f0(signal, frame_duration=0.05)