Repository: pyfar/pyfar
Pull request #663 (2024-08-13 06:00:42)

change squeeze in gammatone process method
Adjusted `GammatoneBands` class to keep the cshape of the input signal and have it consistent with the filter classes (see #658).




hoyer-a: I added tests for the shape, once #664 is merged i will include the `shape=(1, 1)` case.

Patch:
diff --git a/pyfar/dsp/filter/gammatone.py b/pyfar/dsp/filter/gammatone.py
index f9d20e34..1986b827 100644
--- a/pyfar/dsp/filter/gammatone.py
+++ b/pyfar/dsp/filter/gammatone.py
@@ -309,7 +309,7 @@ class GammatoneBands():
             delay_samples + 3, sampling_rate=self.sampling_rate))
 
         # compute the envelope
-        ir = real.time + 1j * imag.time
+        ir = real.time[:, 0, :] + 1j * imag.time[:, 0, :]
         env = np.abs(ir)
 
         # sample at which the maximum occurs
@@ -444,10 +444,6 @@ class GammatoneBands():
         time_out = np.reshape(time_out,
                               (self.n_bands, ) + signal.cshape + (-1, ))
 
-        # squeeze dimension of single channel signal
-        if signal.cshape == (1, ):
-            time_out = np.squeeze(time_out)
-
         # return real and immaginary part of output as pyfar Signal objects
         real = signal.copy()
         real.time = np.real(time_out)


Test patch:
diff --git a/tests/test_filter_gammatone.py b/tests/test_filter_gammatone.py
index d376a1d7..0bab5326 100644
--- a/tests/test_filter_gammatone.py
+++ b/tests/test_filter_gammatone.py
@@ -69,9 +69,9 @@ def test_gammatone_bands_init_and_getter():
 
 
 @pytest.mark.parametrize('amplitudes,shape_filtered,sampling_rate', (
-    [np.array([1]), (85, 2048), 44100],
-    [np.array([1]), (85, 2048), 48000],
-    [np.array([1]), (85, 2048), 96000],
+    [np.array([1]), (85, 1, 2048), 44100],
+    [np.array([1]), (85, 1, 2048), 48000],
+    [np.array([1]), (85, 1, 2048), 96000],
     [np.array([[1, 2], [3, 4]]), (85, 2, 2, 2048), 44100]
 ))
 def test_gammatone_bands_roundtrip(amplitudes, shape_filtered, sampling_rate):
@@ -122,11 +122,11 @@ def test_gammatone_bands_reset_state():
         pf.Signal(np.zeros(2**11), 44100), reset=False)
 
     # check for equality
-    npt.assert_array_equal(real_a.time, real.time[:, :2**11])
-    npt.assert_array_equal(imag_a.time, imag.time[:, :2**11])
+    npt.assert_array_equal(real_a.time, real.time[:, :, :2**11])
+    npt.assert_array_equal(imag_a.time, imag.time[:, :, :2**11])
 
-    npt.assert_array_equal(real_b.time, real.time[:, -2**11:])
-    npt.assert_array_equal(imag_b.time, imag.time[:, -2**11:])
+    npt.assert_array_equal(real_b.time, real.time[:, :, -2**11:])
+    npt.assert_array_equal(imag_b.time, imag.time[:, :, -2**11:])
 
 
 def test_gammatone_bands_assertions():
@@ -165,6 +165,18 @@ def test_gammatone_bands_repr():
                         "@ 44100 Hz sampling rate")
 
 
+@pytest.mark.parametrize('shape', [(4, 1), (1, 4), (1,), (1, 1)])
+def test_gammatone_bands_shape(shape):
+    """Test the shape of GammatoneBands-filtered signals"""
+    impulse = pf.signals.impulse(2048, 0, np.ones(shape))
+    GFB = filter.GammatoneBands([0, 22050])
+
+    real, imag = GFB.process(impulse)
+
+    assert real.time.shape == (GFB.n_bands, *shape, impulse.n_samples)
+    assert imag.time.shape == (GFB.n_bands, *shape, impulse.n_samples)
+
+
 def test_erb_frequencies():
     """Test erb_frequencies against reference from the AMT toolbox"""