Skip to content

Commit

Permalink
Remove numpy, separate eval and plotting
Browse files Browse the repository at this point in the history
  • Loading branch information
HEnquist committed Oct 21, 2020
1 parent e4867a1 commit 646a710
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 76 deletions.
1 change: 1 addition & 0 deletions camilladsp_plot/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from camilladsp_plot.plot_filters import plot_filters, plot_filter, plot_filterstep, plot_all_filtersteps
from camilladsp_plot.plot_pipeline import plot_pipeline
from camilladsp_plot.eval_filterconfig import eval_filter, eval_filterstep
72 changes: 72 additions & 0 deletions camilladsp_plot/eval_filterconfig.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import math
import cmath
from camilladsp_plot.filters import Biquad, BiquadCombo, Conv, DiffEq, Gain


def logspace(minval, maxval, npoints):
logmin = math.log10(minval)
logmax = math.log10(maxval)
perstep = (logmax-logmin)/npoints
values = [10.0**(logmin+n*perstep) for n in range(npoints)]
return values

def eval_filter(filterconf, name=None, samplerate=44100, npoints=1000):
fvect = logspace(1.0, samplerate*0.95/2.0, npoints)
result = {"name": name, "samplerate": samplerate, "f": fvect }
if name is None:
name = "unnamed {}".format(filterconf['type'])
if filterconf['type'] in ('Biquad', 'DiffEq', 'BiquadCombo'):
if filterconf['type'] == 'DiffEq':
currfilt = DiffEq(filterconf['parameters'], samplerate)
elif filterconf['type'] == 'BiquadCombo':
currfilt = BiquadCombo(filterconf['parameters'], samplerate)
else:
currfilt = Biquad(filterconf['parameters'], samplerate)

_fplot, magn, phase = currfilt.gain_and_phase(fvect)
result["magnitude"] = magn
result["phase"] = phase
elif filterconf['type'] == 'Conv':
if 'parameters' in filterconf:
currfilt = Conv(filterconf['parameters'], samplerate)
else:
currfilt = Conv(None, samplerate)
_ftemp, magn, phase = currfilt.gain_and_phase(fvect)
t, impulse = currfilt.get_impulse()
result["magnitude"] = magn
result["phase"] = phase
result["time"] = t
result["impulse"] = impulse
return result

def eval_filterstep(conf, pipelineindex, name="filterstep", npoints=1000, toimage=False):

samplerate = conf['devices']['samplerate']
fvect = logspace(1.0, samplerate*0.95/2.0, npoints)
pipelinestep = conf['pipeline'][pipelineindex]
totcgain=[1.0 for n in range(npoints)]
for filt in pipelinestep['names']:
filterconf = conf['filters'][filt]
if filterconf['type'] == 'DiffEq':
currfilt = DiffEq(filterconf['parameters'], samplerate)
elif filterconf['type'] == 'BiquadCombo':
currfilt = BiquadCombo(filterconf['parameters'], samplerate)
elif filterconf['type'] == "Biquad":
currfilt = Biquad(filterconf['parameters'], samplerate)
elif filterconf['type'] == "Conv":
currfilt = Conv(filterconf['parameters'], samplerate)
elif filterconf['type'] == "Gain":
currfilt = Gain(filterconf['parameters'])
else:
continue
_, cgainstep = currfilt.complex_gain(fvect)
totcgain = [cg * cgstep for (cg, cgstep) in zip(totcgain, cgainstep)]
gain = [20.0 * math.log10(abs(cg) + 1e-15) for cg in totcgain]
phase = [180 / math.pi * cmath.phase(cg) for cg in totcgain]
result = {"name": name, "samplerate": samplerate, "f": fvect, "magnitude": gain, "phase": phase}
return result





62 changes: 40 additions & 22 deletions camilladsp_plot/filter_eval.py → camilladsp_plot/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import csv
import yaml
import sys
from matplotlib import pyplot as plt
import math
import itertools
import struct
Expand All @@ -12,15 +11,24 @@

class Conv(object):

DATATYPE = {
TYPES_DIRECT = {
"FLOAT64LE": "<d",
"FLOAT32LE": "<f",
"S16LE": "<h",
"S24LE": "<i",
"S24LE3": "<i",
"S32LE": "<i",
}

TYPES_INDIRECT = {
"S24LE": {
"pattern": "sssx",
"endian": "little"
},
"S24LE3": {
"pattern": "sss",
"endian": "little"
},
}

SCALEFACTOR = {
"FLOAT64LE": 1.0,
"FLOAT32LE": 1.0,
Expand Down Expand Up @@ -64,7 +72,12 @@ def _read_coeffs(self, conf):
if conf["format"] == "TEXT":
values = self._read_text_coeffs(fname, skip_nbr, read_nbr)
else:
values = self._read_binary_coeffs(fname, conf["format"], skip_nbr, read_nbr)
if conf["format"] in self.TYPES_DIRECT:
values = self._read_binary_direct_coeffs(fname, conf["format"], skip_nbr, read_nbr)
elif conf["format"] in self.TYPES_INDIRECT:
values = self._read_binary_indirect_coeffs(fname, conf["format"], skip_nbr, read_nbr)
else:
raise ValueError(f"Unsupported format {conf['format']}")
return values

def _read_text_coeffs(self, fname, skip_lines, read_lines):
Expand All @@ -73,25 +86,36 @@ def _read_text_coeffs(self, fname, skip_lines, read_lines):
values = [float(row[0]) for row in rawvalues]
return values

def _read_binary_coeffs(self, fname, sampleformat, skip_bytes, read_bytes):
def _read_binary_direct_coeffs(self, fname, sampleformat, skip_bytes, read_bytes):

if read_bytes is None:
count = -1
else:
count = read_bytes

datatype = self.DATATYPE[sampleformat]
datatype = self.TYPES_DIRECT[sampleformat]
factor = self.SCALEFACTOR[sampleformat]
with open(fname, 'rb') as f:
f.seek(skip_bytes)
data = f.read(count)
print(data)
values = [float(val[0])/factor for val in struct.iter_unpack(datatype, data)]
values = [float(val[0])/factor for val in struct.iter_unpack(datatype, data)]
return values

def _repack_24bit(self, values):
new_values = values[0::3].astype(np.int8)*2**16 + values[1::3]*2**8 + values[2::3]
return new_values
def _read_binary_indirect_coeffs(self, fname, sampleformat, skip_bytes, read_bytes):

if read_bytes is None:
count = -1
else:
count = read_bytes

pattern = self.TYPES_INDIRECT[sampleformat]["pattern"]
factor = self.SCALEFACTOR[sampleformat]
endian = self.TYPES_INDIRECT[sampleformat]["endian"]
with open(fname, 'rb') as f:
f.seek(skip_bytes)
data = f.read(count)
values = [int.from_bytes(b"".join(val), endian, signed=True)/factor for val in struct.iter_unpack(pattern, data)]
return values

def complex_gain(self, f):
impulselen = len(self.impulse)
Expand All @@ -104,8 +128,9 @@ def complex_gain(self, f):
impfft = fft(impulse)
f_fft = [self.fs*n/(2.0*npoints) for n in range(npoints)]
cut = impfft[0:npoints]
#cut = self.interpolate(cut, f_fft, f)
return f, cut
if f is not None:
interpolated = self.interpolate(cut, f_fft, f)
return f, interpolated
return f_fft, cut

def interpolate(self, y, xold, xnew):
Expand All @@ -121,19 +146,12 @@ def interpolate(self, y, xold, xnew):
if i2>=(len(y)):
i2 = i1
fract = idx - i1

newval = (1-fract)*y[i1] + fract*y[i2]
#newval = y[i1]
ynew.append(newval)
#print(i1, i2, fract, newval)
#print(y[0:20], ynew[0:20])
return ynew




def gain_and_phase(self, f):
f_fft, Avec = self.complex_gain(f)
f_fft, Avec = self.complex_gain(None)
gain = [20 * math.log10(abs(A)) for A in Avec]
gain = self.interpolate(gain, f_fft, f)
phase = [180 / math.pi * cmath.phase(A) for A in Avec]
Expand Down
68 changes: 21 additions & 47 deletions camilladsp_plot/plot_filters.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,40 @@

from camilladsp_plot.filter_eval import Biquad, BiquadCombo, Conv, DiffEq, Gain
import numpy as np
from camilladsp_plot.filters import Biquad, BiquadCombo, Conv, DiffEq, Gain
from camilladsp_plot.eval_filterconfig import eval_filter, eval_filterstep
import matplotlib
from matplotlib import pyplot as plt
import io

def plot_filter(filterconf, name=None, samplerate=44100, npoints=1000, toimage=False):
if toimage:
matplotlib.use('Agg')
fvect = np.logspace(np.log10(1.0), np.log10((samplerate*0.95)/2.0), num=npoints, base=10.0)
if name is None:
name = "unnamed {}".format(filterconf['type'])
filterdata = eval_filter(filterconf, name, samplerate, npoints)
if filterconf['type'] in ('Biquad', 'DiffEq', 'BiquadCombo'):
if filterconf['type'] == 'DiffEq':
currfilt = DiffEq(filterconf['parameters'], samplerate)
elif filterconf['type'] == 'BiquadCombo':
currfilt = BiquadCombo(filterconf['parameters'], samplerate)
else:
currfilt = Biquad(filterconf['parameters'], samplerate)
plt.figure(num=name)
fplot, magn, phase = currfilt.gain_and_phase(fvect)
stable = currfilt.is_stable()
fplot = filterdata["f"]
magn = filterdata["magnitude"]
phase = filterdata["phase"]
plt.subplot(2,1,1)
plt.semilogx(fplot, magn)
plt.title("{}, stable: {}".format(name, stable))
plt.title(f"{name}")
plt.ylabel("Magnitude")
plt.subplot(2,1,2)
plt.semilogx(fvect, phase)
plt.semilogx(fplot, phase)
plt.ylabel("Phase")
elif filterconf['type'] == 'Conv':
if 'parameters' in filterconf:
currfilt = Conv(filterconf['parameters'], samplerate)
else:
currfilt = Conv(None, samplerate)
plt.figure(num=name)
ftemp, magn, phase = currfilt.gain_and_phase(fvect)
fplot = filterdata["f"]
magn = filterdata["magnitude"]
phase = filterdata["phase"]
t = filterdata["time"]
impulse = filterdata["impulse"]
plt.subplot(2,1,1)
plt.semilogx(ftemp, magn)
plt.semilogx(fplot, magn)
plt.title("{}".format(name))
plt.ylabel("Magnitude")
plt.gca().set(xlim=(10, samplerate/2.0))
t, imp = currfilt.get_impulse()
plt.subplot(2,1,2)
plt.plot(t, imp)
plt.plot(t, impulse)
plt.ylabel("Impulse response")
if toimage:
buf = io.BytesIO()
Expand All @@ -60,35 +52,17 @@ def plot_filters(conf):
def plot_filterstep(conf, pipelineindex, name="filterstep", npoints=1000, toimage=False):
if toimage:
matplotlib.use('Agg')
samplerate = conf['devices']['samplerate']
fvect = np.logspace(np.log10(1.0), np.log10((samplerate*0.95)/2.0), num=npoints, base=10.0)
pipelinestep = conf['pipeline'][pipelineindex]
cgain=np.ones(fvect.shape, dtype=complex)
for filt in pipelinestep['names']:
filterconf = conf['filters'][filt]
if filterconf['type'] == 'DiffEq':
currfilt = DiffEq(filterconf['parameters'], samplerate)
elif filterconf['type'] == 'BiquadCombo':
currfilt = BiquadCombo(filterconf['parameters'], samplerate)
elif filterconf['type'] == "Biquad":
currfilt = Biquad(filterconf['parameters'], samplerate)
elif filterconf['type'] == "Conv":
currfilt = Conv(filterconf['parameters'], samplerate)
elif filterconf['type'] == "Gain":
currfilt = Gain(filterconf['parameters'])
else:
continue
_, cgainstep = currfilt.complex_gain(fvect)
cgain = cgain * cgainstep
gain = 20 * np.log10(np.abs(cgain) + 1e-15)
phase = 180 / np.pi * np.angle(cgain)
filterdata = eval_filterstep(conf, pipelineindex, name, npoints)
fplot = filterdata["f"]
magn = filterdata["magnitude"]
phase = filterdata["phase"]
plt.figure(num=name)
plt.subplot(2,1,1)
plt.semilogx(fvect, gain)
plt.semilogx(fplot, magn)
plt.title(name)
plt.ylabel("Magnitude")
plt.subplot(2,1,2)
plt.semilogx(fvect, phase)
plt.semilogx(fplot, phase)
plt.ylabel("Phase")
if toimage:
buf = io.BytesIO()
Expand Down
4 changes: 0 additions & 4 deletions camilladsp_plot/plot_pipeline.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
# show_config.py

import numpy as np
import numpy.fft as fft
import csv
import yaml
import sys
Expand Down
10 changes: 8 additions & 2 deletions camilladsp_plot/plotcamillaconf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@
import sys
from camilladsp_plot.plot_pipeline import plot_pipeline
from camilladsp_plot.plot_filters import plot_filters, plot_all_filtersteps
from matplotlib import pyplot as plt
try:
from matplotlib import pyplot as plt
except ImportError:
plt = None

def main():
if plt is None:
print("Matplotlib is not available! Can't display plots.")
return
fname = sys.argv[1]

conffile = open(fname)
Expand All @@ -13,7 +19,7 @@ def main():

plot_pipeline(conf)
plot_filters(conf)
#plot_all_filtersteps(conf)
plot_all_filtersteps(conf)

plt.show()

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
url="https://github.com/HEnquist/pycamilladsp-plot",
packages=setuptools.find_packages(),
python_requires=">=3",
install_requires=["PyYAML", "numpy", "matplotlib"],
install_requires=["PyYAML"],
entry_points = {
'console_scripts': ['plotcamillaconf=camilladsp_plot.plotcamillaconf:main'],
}
Expand Down

0 comments on commit 646a710

Please sign in to comment.