2015-10-18 23:06:39 -07:00
|
|
|
from .util import *
|
|
|
|
from .bq import *
|
2015-10-19 18:22:49 -07:00
|
|
|
from .data import *
|
2015-10-28 04:04:31 -07:00
|
|
|
from .nsf import *
|
2015-10-18 23:06:39 -07:00
|
|
|
from .sweeps import *
|
|
|
|
from .smoothfft import *
|
|
|
|
from .plot import *
|
|
|
|
from .wav import *
|
|
|
|
from .planes import *
|
|
|
|
from .fft import *
|
|
|
|
from .bs import *
|
2015-10-26 04:04:32 -07:00
|
|
|
from .cepstrum import *
|
2015-10-18 23:06:39 -07:00
|
|
|
|
2015-10-19 20:53:51 -07:00
|
|
|
import numpy as np
|
|
|
|
from matplotlib.pylab import show
|
|
|
|
|
2015-10-18 23:06:39 -07:00
|
|
|
def analog(b, a):
|
|
|
|
import sympy as sym
|
|
|
|
w,s = sym.symbols('w s')
|
|
|
|
filt_expr = sym.Poly(b, s)/sym.Poly(a, s)
|
|
|
|
mag_expr = abs(filt_expr.subs({s: w*sym.I}))**2
|
|
|
|
return sym.lambdify(w, mag_expr, 'numpy')
|
|
|
|
|
|
|
|
def makemag(w0, ba, gain=0):
|
|
|
|
f = analog(*ba)
|
|
|
|
def magf(w):
|
|
|
|
a = f(w/w0)
|
|
|
|
a[0] = 1e-35
|
|
|
|
a = np.log10(a)*10 + gain
|
|
|
|
a[0] = a[1] # safety measure
|
|
|
|
return a
|
|
|
|
return magf
|
|
|
|
|
|
|
|
def test_filter_raw(ba, fc=1000, gain=0, precision=4096):
|
|
|
|
fig, ax = new_response(ymin=-24, ymax=24)
|
|
|
|
xs = xsp(precision)
|
|
|
|
ax.semilogx(xs, makemag(fc, ba, gain)(xs))
|
|
|
|
|
|
|
|
def test_filter(ff, A=toA(12), Q=toQ(1), **kwargs):
|
|
|
|
test_filter_raw(ff(A, Q), **kwargs)
|
|
|
|
|
|
|
|
def neonpink(xs):
|
2015-10-19 04:04:32 -07:00
|
|
|
lament("neonpink(): DEPRECATED; use tilter2(xs, 'raw') instead.")
|
2015-10-19 20:53:51 -07:00
|
|
|
return tilter2(xs, 'raw')
|
2015-10-18 23:06:39 -07:00
|
|
|
|
|
|
|
def c_render(cascade, precision=4096):
|
2015-10-23 04:04:28 -07:00
|
|
|
# TODO: deprecate in favor of tilter2
|
2015-10-18 23:06:39 -07:00
|
|
|
xs = xsp(precision)
|
2015-10-19 20:53:51 -07:00
|
|
|
return xs, tilter2(xs, cascade)
|
2015-10-18 23:06:39 -07:00
|
|
|
|
2015-10-19 05:39:37 -07:00
|
|
|
def c_render2(xs, cascade, phase=False):
|
2015-10-19 04:04:32 -07:00
|
|
|
"""c_render optimized and specifically for first/second-order filters"""
|
|
|
|
import numexpr as ne
|
|
|
|
j = np.complex(0, 1)
|
2015-10-19 05:39:37 -07:00
|
|
|
eq2 = '(b0 + j*b1*ws - b2*ws**2)/(a0 + j*a1*ws - a2*ws**2)'
|
|
|
|
eq1 = '(b0 + j*b1*ws)/(a0 + j*a1*ws)'
|
|
|
|
if not phase:
|
|
|
|
fmt = 'real(log10(abs({})**2)*10 + gain)'
|
|
|
|
else:
|
|
|
|
fmt = 'arctan2(imag({0}), real({0}))' # gross
|
2015-10-19 04:04:32 -07:00
|
|
|
ys = np.zeros(len(xs))
|
|
|
|
for f in cascade:
|
|
|
|
w0, ba, gain = f
|
|
|
|
b, a = ba
|
|
|
|
if len(b) == 3 and len(a) == 3:
|
|
|
|
eq = fmt.format(eq2)
|
|
|
|
b2, b1, b0 = b
|
|
|
|
a2, a1, a0 = a
|
|
|
|
elif len(b) == 2 and len(a) == 2:
|
|
|
|
eq = fmt.format(eq1)
|
|
|
|
b1, b0 = b
|
|
|
|
a1, a0 = a
|
|
|
|
else:
|
|
|
|
raise Exception("incompatible cascade; consider using c_render instead")
|
|
|
|
ws = xs/w0
|
|
|
|
ys += ne.evaluate(eq)
|
2015-10-19 05:39:37 -07:00
|
|
|
if phase:
|
|
|
|
ys = degrees_clamped(ys)
|
2015-10-19 04:04:32 -07:00
|
|
|
return ys
|
|
|
|
|
2015-10-19 20:53:51 -07:00
|
|
|
def firize(xs, ys, n=4096, srate=44100, ax=None):
|
2015-10-18 23:06:39 -07:00
|
|
|
import scipy.signal as sig
|
2015-10-19 20:53:51 -07:00
|
|
|
if ax:
|
|
|
|
ax.semilogx(xs, ys, label='desired')
|
2015-10-18 23:06:39 -07:00
|
|
|
xf = xs/srate*2
|
|
|
|
yg = 10**(ys/20)
|
|
|
|
|
|
|
|
xf = np.hstack((0, xf, 1))
|
|
|
|
yg = np.hstack((0, yg, yg[-1]))
|
|
|
|
|
|
|
|
b = sig.firwin2(n, xf, yg, antisymmetric=True)
|
|
|
|
|
2015-10-19 20:53:51 -07:00
|
|
|
if ax:
|
2015-10-18 23:06:39 -07:00
|
|
|
_, ys = sig.freqz(b, worN=xs/srate*tau)
|
|
|
|
ys = 20*np.log10(np.abs(ys))
|
2015-10-19 20:53:51 -07:00
|
|
|
ax.semilogx(xs, ys, label='FIR ({} taps)'.format(n))
|
|
|
|
ax.legend(loc=8)
|
2015-10-18 23:06:39 -07:00
|
|
|
|
|
|
|
return b
|
|
|
|
|
|
|
|
def tilter(xs, ys, tilt):
|
|
|
|
"""tilts a magnitude plot by some decibels, or by equalizer curve."""
|
2015-10-19 04:04:32 -07:00
|
|
|
lament("tilter(): DEPRECATED; use ys -= tilter2(xs, tilt) instead.")
|
2015-10-19 20:53:51 -07:00
|
|
|
return xs, ys - tilter2(xs, tilt)
|
2015-10-18 23:06:39 -07:00
|
|
|
|
2015-10-23 04:04:28 -07:00
|
|
|
def tilter2(xs, tilt): # TODO: rename
|
2015-10-19 20:53:51 -07:00
|
|
|
noise = np.zeros(xs.shape)
|
|
|
|
if isinstance(tilt, str) and tilt in cascades:
|
|
|
|
tilt = cascades[tilt]
|
|
|
|
if isinstance(tilt, list):
|
|
|
|
c = [makemag(*f) for f in tilt]
|
2015-10-18 23:06:39 -07:00
|
|
|
for f in c:
|
|
|
|
noise += f(xs)
|
|
|
|
elif isinstance(tilt, int) or isinstance(tilt, float):
|
|
|
|
noise = tilt*(np.log2(1000) - np.log2(xs + 1e-35))
|
|
|
|
return noise
|
|
|
|
|
2015-10-19 04:04:32 -07:00
|
|
|
from .plotwav import *
|
2015-10-18 23:06:39 -07:00
|
|
|
|
|
|
|
# this is similar to default behaviour of having no __all__ variable at all,
|
|
|
|
# but ours ignores modules as well. this allows for `import sys` and such
|
|
|
|
# without clobbering `from our_module import *`.
|
|
|
|
__all__ = [o for o in locals() if type(o) != 'module' and not o.startswith('_')]
|