Transforms Guide - Quefumas/gensound GitHub Wiki

Transforms are objects that apply various effects or actions on existing Signals. Below we describe only the most essential Transforms available for import from gensound or gensound.transforms. Other than these, there are many more specialized Transforms, which will be described in dedicated pages in the wiki.

For defining new Transforms, see here.

Using Transforms

  • To apply a transform to a Signal, use the syntax: SignalObject * TransformObject. Gensound enforces that the Transform always be the right multiplicand, as this makes it visually easier to distinguish between the Signal and the Transform applied to it. (There is one exception to this syntax, see below)

  • Transforms may be chained together: SomeSignal()*Transform1()*Transform2(); they will be applied in the same order to the Signal object.

  • When using the same set of Transforms in conjunction very often, it is useful to give a shorter name: my_effect = Transform1(...)*Transform2(...)*Transform3(...). The name my_effect may then be treated as any other Transform: SignalObject * my_effect.

  • Just like Signal objects, Transforms behave like immutable objects:

a = Transform1(...)
b = a
b *= Transform2(...) # 'a' remains unchanged

Volume/Level

Adjusting Amplitude

To scale the amplitude (i.e., volume) of a Signal object, left-multiply by a float. This is a special syntax that is dedicated for this common case.

0.2*Sine() # Sine wave ranging from -0.2 to 0.2

This logic also generalizes to the following cases:

1*Sine() # has no effect

0*Sine() # will result in silence

-Sine() # inverts phase (will sounds the same if played as it is)

Sine() - Sine() # will result in silence

Adjusting Level (dBs)

To adjust the volume level using decibels (dBs) rather than amplitude, use the Gain transform. dBs correlate much better than amplitude with our subjective perception of loudness.

s = WAV(test_wav)*Gain(-3)
s.play()

Gain receives a single argument, the amount of dBs by which to adjust the volume. Supplying a positive number will increase the volume by the specified amount of dBs, and a negative number will decrease it. 0 leaves the volume level unchanged, while the theoretical minimum of minus infinity would result in complete silence.

Fade in/out

The Transforms FadeIn and FadeOut accept the following arguments:

  • duration - the time duration from complete silence to the signal's full amplitude (or the other way round for the case of FadeOut). Accepts milliseconds (float) or samples (int) as usual.
  • curve - the 'shape' of the fade, determining the exact nature of the shift from silence to full amplitude. Default is linear, and see the Fade Curves page for the complete list and definitions.
  • Other, curve-specific arguments (see above link).
from gensound import test_wav, WAV, FadeIn, FadeOut

w = WAV(test_wav)[10e3:20e3]*FadeIn(duration=1e3)*FadeOut(duration=2e3)
w.play()

Crossfade

Cross-fade is used to seamlessly merge two bits of audio together. Since this is a Transform that affects two Signals simultaneously, it uses a unique syntax: Signal | CrossFade | Signal. This will fade out the first Signal while fading in the second Signal. It receives the same arguments as FadeIn and FadeOut. Here, duration indicates the total time where both signals overlap. Thus, the resulting audio in the example below will only last 15 seconds.

from gensound import test_wav, WAV, CrossFade
w = WAV(test_wav)
s = w[10e3:20e3] | CrossFade(duration=5e3) | w[20.5e3:30.5e3]
s.play()

Note that the default curve for CrossFade is polynomial with degree=0.5, which was chosen since it evens the loudness throughout the fade, though this may change if both Signals are highly correlated.

Time Dimension

Reverse

To play audio in reverse, apply the Reverse transform. In the example code below, the L channel will be played in reverse, with the R channel remaining untouched:

w = WAV(test_wav)
w[0] *= Reverse()
w.play()

Extending Audio

To extend a signal by appending silence, apply the Extend transform. It receives the single argument duration, which, as usual, can accept either floats (milliseconds) or ints (number of samples).

The same effect can also be achieved by concatenating a Silence Signal. These are incidentally the same, adding a second of silence at the end of the sine wave:

Sine() | Silence(1e3)
Sine()*Extend(1e3)

TODO what happens if we input a negative number? Currently would probably raise an error, but this can be taken to mean removing part of the signal. Although this is a useful action, this can also be done by Signal slicing, and also the name Extend wouldn't really fit any more.

Shifting Audio Around

To push Signals forward or backward in time, use the Shift Transform. It receives the usual duration argument, which indicates the amount of time by which to displace the Signal. Negative numbers will make the Signal appear earlier than expected. See the following examples, and note how this interacts with playback, concatenating and mixing:

w = WAV(test_wav)[10e3:15e3]

# add 2 seconds of silence at the beginning
(w*Shift(2e3)).play()

# mix two copies of the same signal, one delayed and played softer by adjusting the amplitude
(w + 0.5*w*Shift(1e3)).play()

# when concatenating two Signals, we can use negative Shift to create overlap:
(w | w*Shift(-2.5e3)).play() # 2.5 secs of overlap

# Haas effect, delaying L channel by a short period makes the sound appear to be coming from the right:
w[0] *= Shift(80) # shift by 80 samples
w.play() # use headphones for best effect

Trying to output a Signal which starts at a negative time will simply cause the playback or export to start from that specific time; so there will be no noticeable effect:

# playback will start at -2s as well, so there will be no noticeable effect
(w*Shift(-2e3)).play()

# if you want to cut the beginning of the Signal, just do this:
w[2e3:].play()