music.adsr

music.adsr(duration=2, attack_ms=20, decay_ms=20, sustain_level=-5, release_ms=50, transition='exp', alpha=1, db_dev=-80, to_zero_ms=1, number_of_samples=None, sonic_vector=0, sample_rate=None, **renamed)[source]

Synthesize an ADSR envelope.

ADSR (Attack, Decay, Sustain, Release) is a very traditional loudness envelope in sound synthesis [1].

Parameters:
durationscalar

The duration of the envelope in seconds.

attack_msscalar

The duration of the Attack in milliseconds.

decay_msscalar

The duration of the Decay in milliseconds.

sustain_levelscalar

The Sustain level after the Decay in decibels. Usually negative.

release_msscalar

The duration of the Release in milliseconds.

transitionstr

“exp” or “exponential” for exponential transitions of amplitude (linear loudness); “lin” or “linear” for linear transitions of amplitude.

alphascalar or array_like

An index to make the exponential fade slower or faster [1]. Ignored if transition=”linear” or alpha=1. One value for every stage, or three: for the Attack, the Decay and the Release.

db_devscalar or array_like

The decibels deviation to reach before using a linear fade to reach zero amplitude. One value for both, or two: for the Attack and the Release. Ignored if transition=”linear”.

to_zero_msscalar or array_like

The duration in milliseconds for linearly departing from zero in the Attack and reaching the value of zero at the end of the Release. One value for both, or two: for the Attack and the Release. Longer than its stage, it is the whole stage. Ignored if transition=”linear”.

number_of_samplesinteger or None

The number of samples of the envelope. If given, duration is ignored, and zero is an envelope of no samples.

sonic_vectorarray_like

Samples for the ADSR envelope to be applied to: mono, or of shape (nchannels, nsamples), every channel shaped alike. If supplied, duration and number_of_samples are ignored.

sample_rateinteger or None

The sample rate. None, the default, is the rate music.get_sample_rate() returns.

**renamed

attack_duration, decay_duration, release_duration and to_zero, the names these milliseconds had before they said their unit among parameters in seconds, and envelope_duration, what duration was called before it took the name every other envelope gives it. Still accepted, with a DeprecationWarning.

Returns:
ndarray

The envelope, a value for each sample; or, when sonic_vector is given, the sound with the envelope applied to it.

Raises:
ValueError

If transition is not one of the four names above. It is checked here because a stage of zero length never reaches fade() or loud(), so an envelope of no stages accepted any name.

If alpha holds other than one or three values, or db_dev or to_zero_ms other than one or two. The per-stage forms were documented since MASS and implemented in neither it nor here, so a sequence reached numpy as a broadcast error.

If alpha is negative and the transition exponential: it bends a transition from zero, where a negative power has no finite value, and the envelope came back holding infinity.

If a duration in milliseconds is negative. A negative attack, release or to_zero_ms was refused by fade() in words about a percentage, and a negative decay made the envelope longer than it was asked to be.

TypeError

If a keyword is neither a parameter nor one of the old names.

See also

tremolo

An oscillation of loudness.

loud

A loudness transition.

fade

A fade in or fade out.

Notes

The envelope is the article’s eq:adsr [1] in its exponential form. With \(\xi = 10^{d/20}\), the level db_dev names, and \(a_S = 10^{S/20}\), sustain_level’s, over the attack’s \(\Lambda_A\) samples, the decay’s \(\Lambda_D\) and the release’s \(\Lambda_R\), each bent by its alpha:

\[ \begin{align}\begin{aligned}a_i = \xi^{\left(1 - \frac{i}{\Lambda_A - 1}\right)^{\alpha_A}}, \qquad a_{\Lambda_A + k} = a_S^{\left(\frac{k}{\Lambda_D - 1} \right)^{\alpha_D}},\\a_S \text{ while it sustains}, \qquad a_{\Lambda - \Lambda_R + k} = a_S \, \xi^{\left( \frac{k}{\Lambda_R - 1}\right)^{\alpha_R}}.\end{aligned}\end{align} \]

At \(\alpha = 1\) the attack is the article’s \(\xi (1/\xi)^{i / (\Lambda_A - 1)}\). The sustain lasts what the other three stages leave, and they are shortened in proportion when they would outlast the envelope. Two things the article does not have: the release falls by \(\xi\) from the sustain level, as the MASS reference has it, where the article’s ends at \(\xi\) itself (DISCREPANCIES.md); and over the attack’s first to_zero_ms and the release’s last, a straight line meets zero, which an exponential never reaches. transition="lin" makes each stage a straight line. Given a sonic_vector \(t\), it returns \(t_i a_i\) (eq:adsrApl).

Cite the following article whenever you use this function.

References

[1]

Fabbri, Renato, et al. “Musical elements in the discrete-time representation of sound.” arXiv preprint arXiv:abs/1412.6853 (2017)

Examples

>>> write_wav_mono(note_with_vibrato() * adsr())
>>> s = horizontal_stack(*[note_with_vibrato() *
...                       adsr(attack_ms=i, release_ms=j)
...                       for i, j in zip([6, 50, 300], [100, 10, 200])])
>>> s = horizontal_stack(*[adsr(attack_ms=i, release_ms=j,
...                            sonic_vector=note_with_vibrato())
...                       for i, j in zip([6, 15, 100], [2, 2, 20])])
>>> envelope = adsr(duration=10, attack_ms=3e3,
...                 decay_ms=0, release_ms=5e3)
>>> stereo = localize(note(duration=1))
>>> adsr(sonic_vector=stereo).shape == stereo.shape   # each channel
True
>>> soft_attack = adsr(alpha=(3, 1, 1), to_zero_ms=(5, 1))  # per stage