PolyBLEP oscillators and chunked synthesis

music.core.synths.polyblep adds an opt-in discontinuity correction for sawtooth and square oscillators with sample-by-sample instantaneous pitch. Existing MASS-compatible synthesis functions, default samples and public interfaces are not modified.

import numpy as np
from music.core.synths.polyblep import (
    PolyBLEPOscillator, polyblep_frequency_path,
)

sample_rate = 48000
frequency = np.full(sample_rate, 10001.)
audio = polyblep_frequency_path(
    frequency, waveform="sawtooth", sample_rate=sample_rate)

oscillator = PolyBLEPOscillator(
    sample_rate=sample_rate, waveform="sawtooth")
chunk_a = oscillator.render(frequency[:500])
chunk_b = oscillator.render(frequency[500:])
np.testing.assert_allclose(
    np.concatenate((chunk_a, chunk_b)), audio, atol=1e-8)

The persistent generator stores its last phase and previous instantaneous frequency. Phase integration is trapezoidal across sample boundaries, including chunk boundaries; empty blocks do not advance phase. This is memory-bounded, chunked offline rendering, not a hard real-time callback implementation or a streaming file reader. Chunk sizes must be under five million samples. A long session can be generated in small buffers with bounded working arrays.

Discontinuity alias suppression

PolyBLEP applies a short polynomial correction around each waveform edge, reducing the spectral foldover due to naive sawtooth/square discontinuities. It costs more per sample than an uncorrected phase comparison, but does not sum every Fourier harmonic as an additive synthesizer does.

The benchmark uses a coherent one-second signal, carrier frequencies just over 0.208 times the sample rate, and separate tests at 44.1, 48 and 96 kHz. It measures the energy in Fourier bins outside DC and the harmonics legitimately below Nyquist. This is a useful aliasing proxy for these static frequencies, not a general sideband-separation method for moving pitch.

python tools/benchmark_polyblep.py --json polyblep-results.json

It compares the naive discontinuous oscillator, PolyBLEP and MUSIC’s opt-in Fourier-harmonic-limited alternative. Reports include RMS, folded-energy fraction, warm median rendering time and peak memory observed by Python tracemalloc. That last measure is not complete native-process peak RSS or a real-time CPU/latency guarantee.

Audible examples, without inferred listener outcomes

python tools/prepare_polyblep_listening.py --out blep-listening

The utility writes RMS-matched, jointly peak-limited PCM24 WAV pairs, randomized A/B order and a blank reviewer form. Only the reviewer directory should be shared with a listener; the organizer directory contains the undisclosed method key. Start at low playback volume. No human evaluation, perceptual preference or neurophysiological outcome is generated by this tool.

Limitations and next decisions

  • Fast FM phase sidebands and nonlinear post-processing can still exceed Nyquist. PolyBLEP corrects waveform steps, not arbitrary modulation or nonlinearities.

  • A square wave at very high frequency is under-resolved even with corrections. PolyBLEP minimizes some transition foldover without being an ideal brick-wall band limiter.

  • The current chunked function allocates numpy arrays per block and does not promise latency limits for a real-time audio callback. Streaming pipelines require chunk scheduling, transport timing, memory profiling and hardware evaluation.

  • Only controlled listening trials can establish listener preferences. Static coherent FFT improvement is necessary evidence for a spectral-aliasing claim, not sufficient evidence of audible benefit.

Use the existing independent analytic/high-rate reference matrix in tools/benchmark_dynamic_aliasing.py for FM, abrupt gates and nonlinear synthesis. In particular, do not substitute a PolyBLEP alias-fraction claim for a general FM result.