Skip to content

Commit

Permalink
Merge pull request #1 from HEnquist/numpyless
Browse files Browse the repository at this point in the history
Numpyless
  • Loading branch information
HEnquist committed Oct 29, 2020
2 parents 9d47b4d + 0d79bbd commit 09665d1
Show file tree
Hide file tree
Showing 10 changed files with 320 additions and 182 deletions.
28 changes: 22 additions & 6 deletions README.md
Expand Up @@ -8,10 +8,10 @@ pip install .
Note that on some systems the command is `pip3` instead of `pip`.

## Requirements
This library requires Python 3.6 or newer, and both `numpy` and `matplotlib`.
This library requires Python 3.6 or newer. For plotting configurations with the commandline tool `plotcamillaconf`, it also requires `numpy` and `matplotlib`. These are not required for using only with the web interface.

These are the names of the packages needed:
| Distribution | python | numpy | matplotlib |
| Distribution | python | numpy (optional) | matplotlib (optional) |
|--------------|--------|-------|------------|
| Fedora | python3 | python3-numpy | python3-matplotlib |
| Debian/Raspbian | python3 | python3-numpy | python3-matplotlib |
Expand All @@ -30,8 +30,11 @@ On macOS use either Anaconda or Homebrew. The Anaconda procedure is the same as

For Homebrew, install Python with `brew install python`, after which you can install the needed packages with pip, `pip3 install numpy` etc.

## Plotting a configuration
This library provides the console command `plotcamillaconf`. Once the library is installed, the command should be available in your terminal.



## Plotting a configuration from the command line
This library provides the console command `plotcamillaconf`. Once the library is installed, togehter with the optional dependencies numpy and matplotlib, the command should be available in your terminal.
To use it type:
```sh
plotcamillaconf /path/to/some/config.yml
Expand All @@ -40,7 +43,7 @@ plotcamillaconf /path/to/some/config.yml
This will plot the frequency response of all the defined filters, and show a block diagram of the pipeline.


## Evaluating filters
## Plotting filters
To plot the frequency response of a filter, use the function `plot_filter`. This is mostly meant for internal use by the `plotcamillaconf` command.
```python
plot_filter(filterconf, name=None, samplerate=44100, npoints=1000, toimage=False)
Expand All @@ -51,7 +54,7 @@ It's also possible to plot the combined frequency response of a Filter step in t
```python
plot_filterstep(conf, pipelineindex, name="filterstep", npoints=1000, toimage=False)
```
This command takes the full configuration as `conf` must be provided. This will plot the step with index `pipelineindex` in the pipeline where 0 is the first step. The `name` is used for the plot title. The number of points in the plot is set with `npoints`. If `toimage` is set to True, then it will instead return the plot as an svg image.
This command takes a full configuration as `conf`. It will then plot the step with index `pipelineindex` in the pipeline where 0 is the first step. The `name` is used for the plot title. The number of points in the plot is set with `npoints`. If `toimage` is set to True, then it will instead return the plot as an svg image.

## Plotting the pipeline
To plot a block diagram of the pipeline, use the function `plot_pipeline`. This is mostly meant for internal use by the `plotcamillaconf` command.
Expand All @@ -60,3 +63,16 @@ plot_pipeline(conf, toimage=False)
```
This takes a full CamillaDSP configuration, `conf`. It will then plot the pipeline using PyPlot. If `toimage` is set to True, then it will instead return the plot as an svg image.

## Evaluating filters
To evaluate the frequency response of a filter, use the function `eval_filter`. This is mostly meant for internal use by the `plotcamillaconf` command as well as the web gui.
```python
eval_filter(filterconf, name=None, samplerate=44100, npoints=1000)
```
This will evaluate the filter and return the result as a dictionary. The filter configuration `filterconf` must be provided. The `samplerate` defaults to 44100 if not given. The filter `name` is used for labels. The number of points in the plot is set with `npoints`. The contents of the returned dictionary depends on the filter type. A Biquad returns `name`, `samplerate`, `f`, `magnitude` and `phase`. A Conv filter additionally return the impulse response in `time` and `impulse`.

It's also possible to evaluate the combined frequency response of a Filter step in the pipeline.
```python
eval_filterstep(conf, pipelineindex, name="filterstep", npoints=1000)
```
This command takes a full configuration as `conf`. It will evaluate the step with index `pipelineindex` in the pipeline where 0 is the first step. As for eval_filter, the result is returned as a dictionary with the same fields as for a Biquad.

1 change: 1 addition & 0 deletions camilladsp_plot/__init__.py
@@ -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
29 changes: 29 additions & 0 deletions camilladsp_plot/cooley_tukey.py
@@ -0,0 +1,29 @@
# Adapted from https://jeremykun.com/2012/07/18/the-fast-fourier-transform/
import cmath
import math

def omega(p, q):
return cmath.exp((2.0 * cmath.pi * 1j * q) / p)

def _fft(signal):
n = len(signal)
if n == 1:
return signal
else:
Feven = _fft([signal[i] for i in range(0, n, 2)])
Fodd = _fft([signal[i] for i in range(1, n, 2)])

combined = [0] * n
for m in range(int(n/2)):
combined[m] = Feven[m] + omega(n, -m) * Fodd[m]
combined[m + int(n/2)] = Feven[m] - omega(n, -m) * Fodd[m]

return combined

def fft(signal):
orig_len = len(signal)
fft_len = 2**(math.ceil(math.log2(orig_len)))
for _n in range(fft_len-orig_len):
signal.append(0.0)
fftsig = _fft(signal)
return fftsig
72 changes: 72 additions & 0 deletions camilladsp_plot/eval_filterconfig.py
@@ -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)
if name is None:
name = "unnamed {}".format(filterconf['type'])
result = {"name": name, "samplerate": samplerate, "f": fvect }
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





0 comments on commit 09665d1

Please sign in to comment.