plt.title(title)
    plt.xlabel('Frequency [Hz]')
    plt.ylabel('Magnitude [dB]')
    plt.legend(legend)


fs = 44100
b, a = filters.calcCoefsLPF2(1000, 10, fs)
plotNonlinearFilterResponse(b, a, 'Nonlinear Resonant Lowpass')
plt.ylim(-15)
plt.grid()
plt.savefig('Pics/NL-LPF.png')

#%%
r = 2
adsp.plot_static_curve(lambda x: np.tanh(x), gain=r)
adsp.plot_static_curve(lambda x: adsp.soft_clipper(x), gain=r)
adsp.plot_static_curve(lambda x: adsp.hard_clipper(x), gain=r)

plt.title('Comparing Saturating Nonlinearities')
plt.legend(['Tanh Clipper', 'Soft Clipper', 'Hard Clipper'])
plt.xlim(-2, 2)
plt.grid()
plt.savefig('Pics/Sat-NLs.png')

#%%
from matplotlib import patches


def zplane(p, z):
    ax = plt.subplot(111)
# ### Nonlinear Gain
#
# The next thing to consider is that a nonlinear function can also be written
# as a dependent gain. For example, the dependent gain for the $\tanh$
# nonlinearity looks like this:
# 
# $$
# g_{tanh} (x) = \frac{\tanh(x)}{x}
# $$
# 
# For the $\tanh$ nonlinearity (as with many nonlinearities), the dependent
# gain will always be in between 0 and 1, and as the input increases, the
# gain decreases to zero.

#%%
adsp.plot_static_curve (lambda x : np.tanh (x))
adsp.plot_static_curve (lambda x : np.tanh (x)/x)
plt.title (r'Comparing $\tanh$ nonlinearity, to dependent gain')
plt.legend (['Tanh', 'Dependent gain'])

#%% [markdown]
# ### What happens to the poles?
#
# Now for the million dollar question: what happens to the poles of the filter
# when we add the nonlinear elements? The basic answer is that the $a$
# coefficients are multiplied by the dependent gain values. Then the new pole
# locations can be described by
#
# $$
# p_{nonlinear} = \frac{-g_{tanh}(x) a_1 \pm \sqrt{g_{tanh}^2(x) a_1^2 - 4g_{tanh}(x) a_2}}{2}
# $$
Esempio n. 3
0
# for an interesting type of distortion effect.
#
# ## GRU Architecture
#
# A GRU is made up of two simple nonlinearities: the
# sigmoid and $tanh$ nonlinearities, both shown below.
# While these curves look similar, note that the
# sigmoid function goes from 0 to 1, while the $tanh$
# function goes from -1 to 1.

# %%
def sigmoid(x):
    return 1.0 / (1.0 + np.exp(-x))

plt.figure()
adsp.plot_static_curve(sigmoid)
plt.title('Sigmoid Nonlinearity')
plt.savefig('Pics/sigmoid.png')

plt.figure()
adsp.plot_static_curve(np.tanh)
plt.title('Tanh Nonlinearity')
plt.savefig('Pics/tanh.png')

# %% [markdown]
# Using these basic nonlinear building blocks we can
# construct a simple type of GRU known as a "minimal
# gated unit" (introduced by
# [Heck and Salem, 2017](https://arxiv.org/abs/1701.03452)).
# The signal processing architecture for this unit is
# shown below, with "Sg" denoting the sigmoid nonlinearity.
# %%
import numpy as np
import audio_dspy as adsp
import scipy.signal as signal
import matplotlib.pyplot as plt
plt.style.use('dark_background')


# %%
def dropout(x, a):
    if isinstance(x, np.ndarray):
        t = np.copy(x)
        for n in range(len(x)):
            t[n] = dropout(t[n], a)
        return t

    B = np.sqrt(a**3 / 3)
    if x > B:
        return x - B + (B / a)**3
    if x < -B:
        return x + B - (B / a)**3
    return (x / a)**3


adsp.plot_static_curve(lambda x: dropout(x, 0.6), gain=2)
plt.title('Static Curve for Dropout Nonlinearity')
# %%
Esempio n. 5
0
    plt.title(title)
    plt.xlabel('Frequency [Hz]')
    plt.ylabel('Magnitude [dB]')
    plt.legend(legend)


fs = 44100
b, a = filters.calcCoefsLPF2(1000, 10, fs)
plotNonlinearFilterResponse(b, a, 'Nonlinear Resonant Lowpass')
plt.ylim(-15)

#%%
import audio_dspy as adsp

r = 2
adsp.plot_static_curve(lambda x: np.tanh(x), range=r)
adsp.plot_static_curve(lambda x: 1.5 * adsp.soft_clipper(x), range=r)
adsp.plot_static_curve(lambda x: adsp.hard_clipper(x), range=r)

plt.title('Comparing Saturating Nonlinearities')
plt.legend(['Tanh Clipper', 'Soft Clipper', 'Hard Clipper'])

#%%
from matplotlib import patches


def zplane(p, z):
    ax = plt.subplot(111)
    uc = patches.Circle((0, 0),
                        radius=1,
                        fill=False,
Esempio n. 6
0
# %%
def tri_wave(x, freq, fs):
    p = float((1 / freq) * fs)
    x = x + p / 4
    return 4 * np.abs((x / p) - np.floor((x / p) + 0.5)) - 1


def sine_wave(x, freq, fs):
    return np.sin(2 * np.pi * x * freq / fs)


# %%
fs = 44100

plt.figure()
adsp.plot_static_curve(lambda x: tri_wave(x, fs / 2, fs), gain=5)
plt.title('Triangle Wavefolder Static Curve')

plt.figure()
adsp.plot_static_curve(lambda x: sine_wave(x, fs / 2, fs), gain=5)
plt.title('Sine Wavefolder Static Curve')

# %% [markdown]
# When we apply this simple wavefolding technique to an input sine
# wave, we get back the expected "folding" behavior.

# %%
N = fs / 50
n = np.arange(N)
freq = 100
x = np.sin(2 * np.pi * n * freq / fs)
Esempio n. 7
0
# at 50 kHz, the signal will be reflected back to $48 - (50 - 48) = 46$
# kHz. This reflected signal is known as an "aliasing artifact" and
# is typically considered undesirable, particularly since the artifact
# is not harmonically related to the original signal. For a more
# in-depth explanation of aliasing, see [here](https://theproaudiofiles.com/digital-audio-aliasing/).
#
# Typically, digital systems use filters to supress any signal above
# the Nyquist frequency, so that aliasing artifacts don't occur. However,
# when using a nonlinear function, the digital system can create signal
# above the Nyquist frequency, thus creating aliasing artifacts. As an
# example let's look at a specific type of distortion known as
# "hard-clipping" distortion. A hard-clipper is a waveshaping distortion
# with a waveshaping curve that looks as follows:

# %%
adsp.plot_static_curve(adsp.hard_clipper, gain=5)
plt.grid()
plt.title('Hard Clipper Response')

# %% [markdown]
# If I now create a 1.2 kHz sine wave, and process it through a
# hard-clipping distortion with no aliasing, I would see the following
# frequency response:

# %%
def plot_fft(x, fs, sm=1.0/24.0):
    fft = 20 * np.log10(np.abs(np.fft.rfft(x) + 1.0e-9))
    freqs = np.fft.rfftfreq(len(x), 1.0 / fs)
    return freqs, fft

def process_nonlin(fc, FS, nonlin, gain=10):