music.render_oversampled

music.render_oversampled(renderer, *, duration=2.0, sample_rate=None, number_of_samples=None, factor=4, **parameters)[source]

Render at a higher sampling rate, low-pass, then downsample.

The renderer must accept number_of_samples and sample_rate, as music.note_with_fm, music.note_with_glissando, and music.isochronic_tones do. It must return samples with time on the last axis, either mono (N,) or channels-first (C, N).

Parameters:
renderercallable()

A sound generator. It is called once at the oversampled rate. Supply nonlinear operations inside it, before decimation.

durationfloat

Output duration in seconds, used when number_of_samples is not given.

sample_rateint or None

Requested output sampling rate in Hz. None, the default, is the rate music.get_sample_rate() returns.

number_of_samplesint or None

Exact output sample count, overriding duration when given; zero is no samples.

factorint

Oversampling multiplier, an integer from 2 to 16.

**parameters

Passed to renderer unchanged, except the sample count and rate.

Returns:
ndarray

Mono or channels-first float64 samples, at exactly the requested count. No peak normalization is applied: preserve output levels.

Raises:
ValueError

On invalid sizes, non-finite samples, or wrong renderer shape.

ImportError

If SciPy is missing. Install with pip install ‘music[antialias]’.

Parameters:
  • renderer (Callable[[...], ArrayLike])

  • duration (float)

  • sample_rate (int | None)

  • number_of_samples (int | None)

  • factor (int)

  • parameters (Any)

Return type:

NDArray[float64]

Notes

Decimates with scipy.signal.resample_poly through a Kaiser-window low-pass that is flat to within 0.0001 dB up to 90 % of the output’s Nyquist frequency and about 100 dB down from that frequency up. What the render makes above the output’s band is removed rather than folded into it, except what folds at the higher rate already; the top tenth of the band is the price. The first and last 64 or so samples are filtered against the silence beyond the render. Filtering smooths intentional hard edges; alias suppression and an infinitely sharp pulse are incompatible at finite sample rate. Always benchmark audio quality and CPU/memory costs for your use case.

Examples

>>> from music import isochronic_tones
>>> signal = render_oversampled(
...     isochronic_tones, sample_rate=48000, duration=.02,
...     carrier_freq=12000, pulse_rate=100)
>>> signal.shape
(960,)