From 1b7c88558dfcdb592db012ce110a5776263aaa9b Mon Sep 17 00:00:00 2001 From: Anton Hoyer Date: Mon, 8 Apr 2024 17:51:19 +0200 Subject: [PATCH 1/9] Wrapper function for private 1st and 2nd order allpass implementations --- pyfar/dsp/filter/__init__.py | 2 + pyfar/dsp/filter/_audiofilter.py | 4 +- pyfar/dsp/filter/audiofilter.py | 132 +++++++++++++++++++++++++++++++ tests/test_filter.py | 38 +++++++++ 4 files changed, 174 insertions(+), 2 deletions(-) diff --git a/pyfar/dsp/filter/__init__.py b/pyfar/dsp/filter/__init__.py index dddbf8fab..b304a83a0 100644 --- a/pyfar/dsp/filter/__init__.py +++ b/pyfar/dsp/filter/__init__.py @@ -9,6 +9,7 @@ ) from .audiofilter import ( + allpass, bell, high_shelve, low_shelve, @@ -29,6 +30,7 @@ __all__ = [ + 'allpass', 'butterworth', 'chebyshev1', 'chebyshev2', diff --git a/pyfar/dsp/filter/_audiofilter.py b/pyfar/dsp/filter/_audiofilter.py index a8edfcfb1..9f98e2eb3 100644 --- a/pyfar/dsp/filter/_audiofilter.py +++ b/pyfar/dsp/filter/_audiofilter.py @@ -285,7 +285,7 @@ def biquad_bs2nd(fm, q, fs, q_warp_method="cos"): return B, A, b, a -def biquad_ap1st(fc, fs, ai=1.): +def biquad_ap1st(fc, fs, ai): """Calc coeff for allpass 1st order. input: @@ -310,7 +310,7 @@ def biquad_ap1st(fc, fs, ai=1.): return B, A, b, a -def biquad_ap2nd(fc, fs, bi=1., ai=np.sqrt(2)): +def biquad_ap2nd(fc, fs, bi, ai): """Calc coeff for allpass 2nd order. input: diff --git a/pyfar/dsp/filter/audiofilter.py b/pyfar/dsp/filter/audiofilter.py index e693acdbd..70edd8572 100644 --- a/pyfar/dsp/filter/audiofilter.py +++ b/pyfar/dsp/filter/audiofilter.py @@ -4,6 +4,138 @@ from . import _audiofilter as iir +def allpass(signal, frequency, order, coefficients=None, sampling_rate=None): + """ + Create and apply first or second order allpass filter. + Transfer function based on Tietze et al. [#]_: + + .. math:: A(s_n) = \\frac{1-\\frac{a_i}{\\omega_c} s_n+\\frac{b_i} + {\\omega_c^2} s_n^2}{1+\\frac{a_i}{\\omega_c} s_n + +\\frac{b_i}{\\omega_c^2} s_n^2} + + By definition the ``bi`` coefficient of a first order allpass is ``0``. + + Uses the implementation of [#]_. + + Parameters + ---------- + signal : Signal, None + The signal to be filtered. Pass ``None`` to create the filter without + applying it. + frequency : number + Cutoff frequency of the allpass in Hz. + order : number + Order of the allpass filter. Must be ``1`` or ``2``. + coefficients: number, list, optional + Filter characteristic coefficients ``bi`` and ``ai``. + + - For 1st order allpass provide ai-coefficient as single value. + - For 2nd order allpass provide coefficients as list ``[bi, ai]``. + + Defaults are chosen according to Tietze et al. (Fig. 12.66) + for maximum flat group delay. + sampling_rate : None, number + The sampling rate in Hz. Only required if signal is ``None``. The + default is ``None``. + Returns + ------- + signal : Signal + The filtered signal. Only returned if ``sampling_rate = None``. + filter : FilterIIR + Filter object. Only returned if ``signal = None``. + References + ---------- + .. [#] Tietze, U., Schenk, C. & Gamm, E. (2019). Halbleiter- + Schaltungstechnik (16th ed.). Springer Vieweg + .. [#] https://github.com/spatialaudio/digital-signal-processing-lecture/\ +blob/master/filter_design/audiofilter.py + + Examples + ----- + First and second order allpass filter with ``fc = 1000`` Hz. + + .. plot:: + + import pyfar as pf + import matplotlib.pyplot as plt + import numpy as np + + # impulse to be filtered + impulse = pf.signals.impulse(256) + + orders = [1, 2] + labels = ['First order', 'Second order'] + + fig, (ax1, ax2) = plt.subplots(2,1, layout='constrained') + + for (order, label) in zip(orders, labels): + # create and apply allpass filter + sig_filt = pf.dsp.filter.allpass(impulse, 1000, order) + pf.plot.group_delay(sig_filt, label=label, ax=ax1) + pf.plot.phase(sig_filt, label=label, ax=ax2, unwrap = True) + + ax2.legend() + """ + + # check input + if (signal is None and sampling_rate is None) \ + or (signal is not None and sampling_rate is not None): + raise ValueError('Either signal or sampling_rate must be none.') + + # check if coefficients match filter order + if coefficients is not None and ( + (order == 1 and np.isscalar(coefficients) is False) + or (order == 2 and ( + type(coefficients) is not list or len(coefficients) != 2))): + print(type(coefficients), order) + raise ValueError('Coefficients must match order') + + # sampling frequency in Hz + fs = signal.sampling_rate if sampling_rate is None else sampling_rate + + if coefficients is None: + # Set default coefficients, see Tietze/ Schenk fig. 12.66 + coeffs_ap1 = 0.6436 + coeffs_ap2 = [0.8832, 1.6278] + elif np.isscalar(coefficients) is True: + # custom coefficients for first order allpass + coeffs_ap1 = coefficients + elif len(coefficients) == 2: + # custom coefficients for second order allpass + coeffs_ap2 = coefficients + + if order == 1: + # get filter coefficients for first order allpass + _, _, b, a = iir.biquad_ap1st(frequency, fs, ai=coeffs_ap1) + filter_coeffs = np.stack((b, a), axis=0) + + # filter object + filt = pf.FilterIIR(filter_coeffs, fs) + filt.comment = ("First order allpass-filter with cutoff frequency" + f" {frequency} Hz.") + + elif order == 2: + # get filter coefficients for second order allpass + _, _, b, a = iir.biquad_ap2nd(frequency, fs, bi=coeffs_ap2[0], + ai=coeffs_ap2[1]) + filter_coeffs = np.stack((b, a), axis=0) + + # filter object + filt = pf.FilterIIR(filter_coeffs, fs) + filt.comment = ("Second order allpass-filter with cutoff frequency" + f" {frequency} Hz.") + else: + raise ValueError('Order must be 1 or 2') + + if signal is None: + # return filter-object + return filt + else: + # return filtered signal + signal_filt = filt.process(signal) + return signal_filt + + def bell(signal, center_frequency, gain, quality, bell_type='II', quality_warp='cos', sampling_rate=None): """ diff --git a/tests/test_filter.py b/tests/test_filter.py index 8d8c66fc1..a19dee475 100644 --- a/tests/test_filter.py +++ b/tests/test_filter.py @@ -127,6 +127,44 @@ def test_bessel(impulse): x = pfilt.bessel(None, 2, 1000, 'lowpass', 'phase') +def test_allpass(impulse): + # Uses third party code. + # We thus only test the functionality not the results + + fc = 1000 + orders = [1, 2] + kinds = ['First', 'Second'] + + for (order, kind) in zip(orders, kinds): + f_obj = pfilt.allpass(None, fc, order, sampling_rate=44100) + assert isinstance(f_obj, pclass.FilterIIR) + assert f_obj.comment == ( + f"{kind} order allpass-filter with cutoff frequency {fc} Hz.") + + # Filter + x = pfilt.allpass(impulse, fc, order) + y = f_obj.process(impulse) + assert isinstance(x, Signal) + npt.assert_allclose(x.time, y.time) + + # test ValueError + with pytest.raises(ValueError): + # pass signal and sampling rate + x = pfilt.allpass(impulse, fc, 1, sampling_rate=44100) + with pytest.raises(ValueError): + # pass no signal and no sampling rate + x = pfilt.allpass(None, fc, 1) + with pytest.raises(ValueError): + # pass wrong order + x = pfilt.allpass(impulse, fc, 3) + with pytest.raises(ValueError): + # pass wrong combination of coefficients and order + x = pfilt.allpass(impulse, fc, 1, [1, 2]) + with pytest.raises(ValueError): + # pass wrong combination of coefficients and order + x = pfilt.allpass(impulse, fc, 2, 1) + + def test_bell(impulse): # Uses third party code. # We thus only test the functionality not the results From 7193d6ee9ae3c81f906010e7b150b3e81058b0d5 Mon Sep 17 00:00:00 2001 From: Anton Hoyer Date: Wed, 10 Apr 2024 12:44:01 +0200 Subject: [PATCH 2/9] changes according to f-brinkmann --- pyfar/dsp/filter/_audiofilter.py | 4 +- pyfar/dsp/filter/audiofilter.py | 70 +++++++++++++++----------------- tests/test_filter.py | 62 +++++++++++++++++++--------- 3 files changed, 77 insertions(+), 59 deletions(-) diff --git a/pyfar/dsp/filter/_audiofilter.py b/pyfar/dsp/filter/_audiofilter.py index 9f98e2eb3..a8edfcfb1 100644 --- a/pyfar/dsp/filter/_audiofilter.py +++ b/pyfar/dsp/filter/_audiofilter.py @@ -285,7 +285,7 @@ def biquad_bs2nd(fm, q, fs, q_warp_method="cos"): return B, A, b, a -def biquad_ap1st(fc, fs, ai): +def biquad_ap1st(fc, fs, ai=1.): """Calc coeff for allpass 1st order. input: @@ -310,7 +310,7 @@ def biquad_ap1st(fc, fs, ai): return B, A, b, a -def biquad_ap2nd(fc, fs, bi, ai): +def biquad_ap2nd(fc, fs, bi=1., ai=np.sqrt(2)): """Calc coeff for allpass 2nd order. input: diff --git a/pyfar/dsp/filter/audiofilter.py b/pyfar/dsp/filter/audiofilter.py index 70edd8572..ba69aee45 100644 --- a/pyfar/dsp/filter/audiofilter.py +++ b/pyfar/dsp/filter/audiofilter.py @@ -7,11 +7,18 @@ def allpass(signal, frequency, order, coefficients=None, sampling_rate=None): """ Create and apply first or second order allpass filter. - Transfer function based on Tietze et al. [#]_: - .. math:: A(s_n) = \\frac{1-\\frac{a_i}{\\omega_c} s_n+\\frac{b_i} - {\\omega_c^2} s_n^2}{1+\\frac{a_i}{\\omega_c} s_n - +\\frac{b_i}{\\omega_c^2} s_n^2} + Allpass filters have an almost constant group delay below their cut-off + frequency and are often used in analogue loudspeaker design. + The filter transfer function is based on Tietze et al. [#]_: + + .. math:: A(s) = \\frac{1-\\frac{a_i}{\\omega_c} s+\\frac{b_i} + {\\omega_c^2} s^2}{1+\\frac{a_i}{\\omega_c} s + +\\frac{b_i}{\\omega_c^2} s^2}, + + + where :math:`\\omega_c = 2 \\pi f_c` with the cut-off frequency :math:`f_c` + and :math:`s=\\sigma + \\mathrm{i} \\omega`. By definition the ``bi`` coefficient of a first order allpass is ``0``. @@ -29,8 +36,11 @@ def allpass(signal, frequency, order, coefficients=None, sampling_rate=None): coefficients: number, list, optional Filter characteristic coefficients ``bi`` and ``ai``. - - For 1st order allpass provide ai-coefficient as single value. - - For 2nd order allpass provide coefficients as list ``[bi, ai]``. + - For 1st order allpass provide ai-coefficient as single value.\n + The default is ``ai = 0.6436``. + + - For 2nd order allpass provide coefficients as list ``[bi, ai]``.\n + The default is ``bi = 1.6278``, ``ai = 0.8832``. Defaults are chosen according to Tietze et al. (Fig. 12.66) for maximum flat group delay. @@ -71,9 +81,10 @@ def allpass(signal, frequency, order, coefficients=None, sampling_rate=None): for (order, label) in zip(orders, labels): # create and apply allpass filter sig_filt = pf.dsp.filter.allpass(impulse, 1000, order) - pf.plot.group_delay(sig_filt, label=label, ax=ax1) + pf.plot.group_delay(sig_filt, unit='samples', label=label, ax=ax1) pf.plot.phase(sig_filt, label=label, ax=ax2, unwrap = True) + ax1.set_title('1. and 2. order allpass filter with fc = 1000 Hz') ax2.legend() """ @@ -85,48 +96,33 @@ def allpass(signal, frequency, order, coefficients=None, sampling_rate=None): # check if coefficients match filter order if coefficients is not None and ( (order == 1 and np.isscalar(coefficients) is False) - or (order == 2 and ( - type(coefficients) is not list or len(coefficients) != 2))): + or (order == 2 and (isinstance(coefficients, (list, np.ndarray)) + is False or len(coefficients) != 2))): print(type(coefficients), order) - raise ValueError('Coefficients must match order') + raise ValueError('Coefficients must match the allpass order') # sampling frequency in Hz fs = signal.sampling_rate if sampling_rate is None else sampling_rate - if coefficients is None: - # Set default coefficients, see Tietze/ Schenk fig. 12.66 - coeffs_ap1 = 0.6436 - coeffs_ap2 = [0.8832, 1.6278] - elif np.isscalar(coefficients) is True: - # custom coefficients for first order allpass - coeffs_ap1 = coefficients - elif len(coefficients) == 2: - # custom coefficients for second order allpass - coeffs_ap2 = coefficients - if order == 1: + if coefficients is None: + coefficients = 0.6436 # get filter coefficients for first order allpass - _, _, b, a = iir.biquad_ap1st(frequency, fs, ai=coeffs_ap1) - filter_coeffs = np.stack((b, a), axis=0) - - # filter object - filt = pf.FilterIIR(filter_coeffs, fs) - filt.comment = ("First order allpass-filter with cutoff frequency" - f" {frequency} Hz.") - + _, _, b, a = iir.biquad_ap1st(frequency, fs, ai=coefficients) elif order == 2: + if coefficients is None: + coefficients = [0.8832, 1.6278] # get filter coefficients for second order allpass - _, _, b, a = iir.biquad_ap2nd(frequency, fs, bi=coeffs_ap2[0], - ai=coeffs_ap2[1]) - filter_coeffs = np.stack((b, a), axis=0) - - # filter object - filt = pf.FilterIIR(filter_coeffs, fs) - filt.comment = ("Second order allpass-filter with cutoff frequency" - f" {frequency} Hz.") + _, _, b, a = iir.biquad_ap2nd(frequency, fs, bi=coefficients[0], + ai=coefficients[1]) else: raise ValueError('Order must be 1 or 2') + filter_coeffs = np.stack((b, a), axis=0) + filt = pf.FilterIIR(filter_coeffs, fs) + filt.comment = (f"Allpass of order {order} with cutoff frequency " + f"{frequency} Hz.") + if signal is None: # return filter-object return filt diff --git a/tests/test_filter.py b/tests/test_filter.py index a19dee475..281d812aa 100644 --- a/tests/test_filter.py +++ b/tests/test_filter.py @@ -127,42 +127,64 @@ def test_bessel(impulse): x = pfilt.bessel(None, 2, 1000, 'lowpass', 'phase') -def test_allpass(impulse): +@pytest.mark.parametrize('order', [1, 2]) +def test_allpass(impulse, order): # Uses third party code. # We thus only test the functionality not the results + fc = 1033 + + sig_filt = pfilt.allpass(impulse, fc, order) + gd = pf.dsp.group_delay(sig_filt) + + # Group delay at w = 0 + T_gr_0 = gd[0] + # definition of group delay at fc (Tietze et al.) + T_fc_desired = T_gr_0 * (1 / np.sqrt(2)) + # id of frequency bin closest to fc + freqs = sig_filt.frequencies + idx = np.argmin(abs(freqs - fc)) + # group delay below fc and at fc + T_below = gd[:idx] + T_fc = gd[idx] + + # tolerance for group delay below fc + tol = 1 - (T_fc / T_gr_0) + + # Test if group delay at fc is correct + np.testing.assert_allclose(T_fc_desired, T_fc, rtol=0.01) + # Test group delay below fc + np.testing.assert_allclose(T_below, T_gr_0, rtol=tol) + + f_obj = pfilt.allpass(None, fc, order, sampling_rate=44100) + assert isinstance(f_obj, pclass.FilterIIR) + assert f_obj.comment == ( + f"Allpass of order {order} with cutoff frequency " + f"{fc} Hz.") - fc = 1000 - orders = [1, 2] - kinds = ['First', 'Second'] - - for (order, kind) in zip(orders, kinds): - f_obj = pfilt.allpass(None, fc, order, sampling_rate=44100) - assert isinstance(f_obj, pclass.FilterIIR) - assert f_obj.comment == ( - f"{kind} order allpass-filter with cutoff frequency {fc} Hz.") + # Filter + x = pfilt.allpass(impulse, fc, order) + y = f_obj.process(impulse) + assert isinstance(x, Signal) + npt.assert_allclose(x.time, y.time) - # Filter - x = pfilt.allpass(impulse, fc, order) - y = f_obj.process(impulse) - assert isinstance(x, Signal) - npt.assert_allclose(x.time, y.time) +def test_allpass_warnings(impulse, fc=1000): # test ValueError with pytest.raises(ValueError): # pass signal and sampling rate - x = pfilt.allpass(impulse, fc, 1, sampling_rate=44100) + _ = pfilt.allpass(impulse, fc, 1, sampling_rate=44100) with pytest.raises(ValueError): # pass no signal and no sampling rate - x = pfilt.allpass(None, fc, 1) + _ = pfilt.allpass(None, fc, 1) with pytest.raises(ValueError): # pass wrong order - x = pfilt.allpass(impulse, fc, 3) + _ = pfilt.allpass(impulse, fc, 3) with pytest.raises(ValueError): # pass wrong combination of coefficients and order - x = pfilt.allpass(impulse, fc, 1, [1, 2]) + _ = pfilt.allpass(impulse, fc, 1, [1, 2]) with pytest.raises(ValueError): # pass wrong combination of coefficients and order - x = pfilt.allpass(impulse, fc, 2, 1) + _ = pfilt.allpass(impulse, fc, 2, 1) def test_bell(impulse): From dc0a324dde6538f6caa889699af2ba42f0a38105 Mon Sep 17 00:00:00 2001 From: Anton Hoyer Date: Sun, 14 Apr 2024 14:19:50 +0200 Subject: [PATCH 3/9] changes according to ahms5 --- pyfar/dsp/filter/audiofilter.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyfar/dsp/filter/audiofilter.py b/pyfar/dsp/filter/audiofilter.py index ba69aee45..acf8ec275 100644 --- a/pyfar/dsp/filter/audiofilter.py +++ b/pyfar/dsp/filter/audiofilter.py @@ -40,19 +40,21 @@ def allpass(signal, frequency, order, coefficients=None, sampling_rate=None): The default is ``ai = 0.6436``. - For 2nd order allpass provide coefficients as list ``[bi, ai]``.\n - The default is ``bi = 1.6278``, ``ai = 0.8832``. + The default is ``bi = 0.8832``, ``ai = 1.6278``. Defaults are chosen according to Tietze et al. (Fig. 12.66) for maximum flat group delay. sampling_rate : None, number The sampling rate in Hz. Only required if signal is ``None``. The default is ``None``. + Returns ------- signal : Signal The filtered signal. Only returned if ``sampling_rate = None``. filter : FilterIIR Filter object. Only returned if ``signal = None``. + References ---------- .. [#] Tietze, U., Schenk, C. & Gamm, E. (2019). Halbleiter- @@ -68,7 +70,6 @@ def allpass(signal, frequency, order, coefficients=None, sampling_rate=None): import pyfar as pf import matplotlib.pyplot as plt - import numpy as np # impulse to be filtered impulse = pf.signals.impulse(256) From 5b9323abf75bd23483c13649577b9fca6c78704d Mon Sep 17 00:00:00 2001 From: Anton Hoyer Date: Thu, 25 Apr 2024 10:43:34 +0200 Subject: [PATCH 4/9] Changes according to f-brinkmanns comments --- pyfar/dsp/filter/audiofilter.py | 7 ++++--- tests/test_filter.py | 28 ++++++++++++++++------------ 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/pyfar/dsp/filter/audiofilter.py b/pyfar/dsp/filter/audiofilter.py index acf8ec275..03b0536a7 100644 --- a/pyfar/dsp/filter/audiofilter.py +++ b/pyfar/dsp/filter/audiofilter.py @@ -96,9 +96,10 @@ def allpass(signal, frequency, order, coefficients=None, sampling_rate=None): # check if coefficients match filter order if coefficients is not None and ( - (order == 1 and np.isscalar(coefficients) is False) - or (order == 2 and (isinstance(coefficients, (list, np.ndarray)) - is False or len(coefficients) != 2))): + (order == 1 and np.isscalar(coefficients) is False) or + (order == 2 and ( + not isinstance(coefficients, (list, np.ndarray)) or + len(coefficients) != 2))): print(type(coefficients), order) raise ValueError('Coefficients must match the allpass order') diff --git a/tests/test_filter.py b/tests/test_filter.py index 281d812aa..c9aa41888 100644 --- a/tests/test_filter.py +++ b/tests/test_filter.py @@ -141,8 +141,7 @@ def test_allpass(impulse, order): # definition of group delay at fc (Tietze et al.) T_fc_desired = T_gr_0 * (1 / np.sqrt(2)) # id of frequency bin closest to fc - freqs = sig_filt.frequencies - idx = np.argmin(abs(freqs - fc)) + idx = sig_filt.find_nearest_frequency(fc) # group delay below fc and at fc T_below = gd[:idx] T_fc = gd[idx] @@ -170,21 +169,26 @@ def test_allpass(impulse, order): def test_allpass_warnings(impulse, fc=1000): # test ValueError - with pytest.raises(ValueError): + with pytest.raises(ValueError, + match='Either signal or sampling_rate must be none.'): # pass signal and sampling rate - _ = pfilt.allpass(impulse, fc, 1, sampling_rate=44100) - with pytest.raises(ValueError): + pfilt.allpass(impulse, fc, 1, sampling_rate=44100) + with pytest.raises(ValueError, + match='Either signal or sampling_rate must be none.'): # pass no signal and no sampling rate - _ = pfilt.allpass(None, fc, 1) - with pytest.raises(ValueError): + pfilt.allpass(None, fc, 1) + with pytest.raises(ValueError, + match='Order must be 1 or 2'): # pass wrong order - _ = pfilt.allpass(impulse, fc, 3) - with pytest.raises(ValueError): + pfilt.allpass(impulse, fc, 3) + with pytest.raises(ValueError, + match='Coefficients must match the allpass order'): # pass wrong combination of coefficients and order - _ = pfilt.allpass(impulse, fc, 1, [1, 2]) - with pytest.raises(ValueError): + pfilt.allpass(impulse, fc, 1, [1, 2]) + with pytest.raises(ValueError, + match='Coefficients must match the allpass order'): # pass wrong combination of coefficients and order - _ = pfilt.allpass(impulse, fc, 2, 1) + pfilt.allpass(impulse, fc, 2, 1) def test_bell(impulse): From 4b8f3d6d04c1f68a417ff32534e80abe5368d6d5 Mon Sep 17 00:00:00 2001 From: hoyer-a <156099087+hoyer-a@users.noreply.github.com> Date: Fri, 26 Apr 2024 12:26:07 +0200 Subject: [PATCH 5/9] Update pyfar/dsp/filter/audiofilter.py Remove real part from laplace variable in docstring Co-authored-by: Marco Berzborn --- pyfar/dsp/filter/audiofilter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfar/dsp/filter/audiofilter.py b/pyfar/dsp/filter/audiofilter.py index 03b0536a7..645b77960 100644 --- a/pyfar/dsp/filter/audiofilter.py +++ b/pyfar/dsp/filter/audiofilter.py @@ -18,7 +18,7 @@ def allpass(signal, frequency, order, coefficients=None, sampling_rate=None): where :math:`\\omega_c = 2 \\pi f_c` with the cut-off frequency :math:`f_c` - and :math:`s=\\sigma + \\mathrm{i} \\omega`. + and :math:`s=\\mathrm{i} \\omega`. By definition the ``bi`` coefficient of a first order allpass is ``0``. From c2b528c4c67fb3bc4c04efcb3b5866ce9e4c59f4 Mon Sep 17 00:00:00 2001 From: hoyer-a <156099087+hoyer-a@users.noreply.github.com> Date: Fri, 26 Apr 2024 12:27:13 +0200 Subject: [PATCH 6/9] Simplify code Co-authored-by: Marco Berzborn --- pyfar/dsp/filter/audiofilter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfar/dsp/filter/audiofilter.py b/pyfar/dsp/filter/audiofilter.py index 645b77960..5e87d8e78 100644 --- a/pyfar/dsp/filter/audiofilter.py +++ b/pyfar/dsp/filter/audiofilter.py @@ -110,7 +110,7 @@ def allpass(signal, frequency, order, coefficients=None, sampling_rate=None): if coefficients is None: coefficients = 0.6436 # get filter coefficients for first order allpass - _, _, b, a = iir.biquad_ap1st(frequency, fs, ai=coefficients) + b, a = iir.biquad_ap1st(frequency, fs, ai=coefficients)[2, 3] elif order == 2: if coefficients is None: coefficients = [0.8832, 1.6278] From ff2bb2641b87d52ab24538ef135e92f35bbdf9a2 Mon Sep 17 00:00:00 2001 From: hoyer-a <156099087+hoyer-a@users.noreply.github.com> Date: Fri, 26 Apr 2024 12:27:28 +0200 Subject: [PATCH 7/9] Simplify code Co-authored-by: Marco Berzborn --- pyfar/dsp/filter/audiofilter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyfar/dsp/filter/audiofilter.py b/pyfar/dsp/filter/audiofilter.py index 5e87d8e78..b5e7ac745 100644 --- a/pyfar/dsp/filter/audiofilter.py +++ b/pyfar/dsp/filter/audiofilter.py @@ -115,8 +115,8 @@ def allpass(signal, frequency, order, coefficients=None, sampling_rate=None): if coefficients is None: coefficients = [0.8832, 1.6278] # get filter coefficients for second order allpass - _, _, b, a = iir.biquad_ap2nd(frequency, fs, bi=coefficients[0], - ai=coefficients[1]) + b, a = iir.biquad_ap2nd( + frequency, fs, bi=coefficients[0], ai=coefficients[1])[2, 3] else: raise ValueError('Order must be 1 or 2') From 29bcac84c0e21010416717abf65e2de61963092c Mon Sep 17 00:00:00 2001 From: Anton Hoyer Date: Fri, 26 Apr 2024 12:47:27 +0200 Subject: [PATCH 8/9] fix failed tests --- pyfar/dsp/filter/audiofilter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyfar/dsp/filter/audiofilter.py b/pyfar/dsp/filter/audiofilter.py index b5e7ac745..1bbb5888a 100644 --- a/pyfar/dsp/filter/audiofilter.py +++ b/pyfar/dsp/filter/audiofilter.py @@ -110,13 +110,13 @@ def allpass(signal, frequency, order, coefficients=None, sampling_rate=None): if coefficients is None: coefficients = 0.6436 # get filter coefficients for first order allpass - b, a = iir.biquad_ap1st(frequency, fs, ai=coefficients)[2, 3] + _, _, b, a = iir.biquad_ap1st(frequency, fs, ai=coefficients) elif order == 2: if coefficients is None: coefficients = [0.8832, 1.6278] # get filter coefficients for second order allpass - b, a = iir.biquad_ap2nd( - frequency, fs, bi=coefficients[0], ai=coefficients[1])[2, 3] + _, _, b, a = iir.biquad_ap2nd( + frequency, fs, bi=coefficients[0], ai=coefficients[1]) else: raise ValueError('Order must be 1 or 2') From f778f6f3a74904776922cf86e2c82f168e7d4353 Mon Sep 17 00:00:00 2001 From: Anton Hoyer Date: Fri, 26 Apr 2024 13:48:33 +0200 Subject: [PATCH 9/9] simplify code again --- pyfar/dsp/filter/audiofilter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyfar/dsp/filter/audiofilter.py b/pyfar/dsp/filter/audiofilter.py index 1bbb5888a..38875876d 100644 --- a/pyfar/dsp/filter/audiofilter.py +++ b/pyfar/dsp/filter/audiofilter.py @@ -110,13 +110,13 @@ def allpass(signal, frequency, order, coefficients=None, sampling_rate=None): if coefficients is None: coefficients = 0.6436 # get filter coefficients for first order allpass - _, _, b, a = iir.biquad_ap1st(frequency, fs, ai=coefficients) + b, a = iir.biquad_ap1st(frequency, fs, ai=coefficients)[2:] elif order == 2: if coefficients is None: coefficients = [0.8832, 1.6278] # get filter coefficients for second order allpass - _, _, b, a = iir.biquad_ap2nd( - frequency, fs, bi=coefficients[0], ai=coefficients[1]) + b, a = iir.biquad_ap2nd( + frequency, fs, bi=coefficients[0], ai=coefficients[1])[2:] else: raise ValueError('Order must be 1 or 2')