Repository: pyfar/pyfar (python)

Issue:
`domain='auto'` option for dsp.normalize?
## General

* pyfar version: 0.6.5

## Description

When using `pyfar.dsp.normalize()` with a FrequencyData object and default values, an error is thrown due to the default `domain='time'`. It would be nice to add an option `domain='auto'`, which checks if `signal` is a FrequencyData object.

## What I Did

```
import pyfar as pf
signal = pf.FrequencyData([1, 2, 3], [1, 2, 3])
pf.dsp.normalize(signal)
```


Pull request:
Fix dsp normalize
### Which issue(s) are closed by this pull request?

Closes #572, Closes #633



Patch:
diff --git a/pyfar/dsp/dsp.py b/pyfar/dsp/dsp.py
index a015d999..ba0d2357 100644
--- a/pyfar/dsp/dsp.py
+++ b/pyfar/dsp/dsp.py
@@ -2079,7 +2079,7 @@ def average(signal, mode='linear', caxis=None, weights=None, keepdims=False,
         return pyfar.FrequencyData(data, signal.frequencies, signal.comment)
 
 
-def normalize(signal, reference_method='max', domain='time',
+def normalize(signal, reference_method='max', domain='auto',
               channel_handling='individual', target=1, limits=(None, None),
               unit=None, return_reference=False, nan_policy='raise'):
     """
@@ -2128,8 +2128,15 @@ def normalize(signal, reference_method='max', domain='time',
           normalized magnitude spectrum is used
           pyfar examples gallery
           (cf. :ref:`FFT normalization<gallery:/gallery/interactive/fast_fourier_transform.ipynb#FFT-normalizations>`).
-
-        The default is ``'time'``.
+        ``'auto'``
+           Uses ``'time'`` domain normalization for
+           :py:class:`Signal <pyfar.classes.audio.Signal>` and
+           :py:class:`TimeData <pyfar.classes.audio.TimeData>` objects and
+           ``'freq'`` domain normalization for
+           :py:class:`FrequencyData <pyfar.classes.audio.FrequencyData>`
+           objects.
+
+        The default is ``'auto'``.
     channel_handling: string, optional
         Define how channel-wise `reference` values are handeled for multi-
         channel signals. This parameter does not affect single-channel signals.
@@ -2236,8 +2243,15 @@ def normalize(signal, reference_method='max', domain='time',
         raise TypeError(("Input data has to be of type 'Signal', 'TimeData' "
                          "or 'FrequencyData'."))
 
-    if domain not in ('time', 'freq'):
-        raise ValueError("domain must be 'time' or 'freq'.")
+    if domain not in ('time', 'freq', 'auto'):
+        raise ValueError("domain must be 'time', 'freq' or 'auto' but is"
+                         f" '{domain}'.")
+    # get signal domain if domain = 'auto'
+    if domain == 'auto' and type(signal) is pyfar.FrequencyData:
+        domain = 'freq'
+    elif domain == 'auto' and (type(signal) is pyfar.TimeData
+                               or type(signal) is pyfar.Signal):
+        domain = 'time'
     if (type(signal) is pyfar.FrequencyData) and domain == 'time':
         raise ValueError((
             f"domain is '{domain}' and signal is type '{signal.__class__}'"


Test patch:
diff --git a/tests/test_dsp_normalize.py b/tests/test_dsp_normalize.py
index f585f57a..dc3857e2 100644
--- a/tests/test_dsp_normalize.py
+++ b/tests/test_dsp_normalize.py
@@ -51,6 +51,19 @@ def test_domains_normalization():
     npt.assert_allclose(np.max(np.abs(freq.freq)), 1)
 
 
+def test_auto_domain_normalization():
+    """Test for normalization with auto domain."""
+    signal = pf.Signal([1, 3, 0], 10)
+    time_data = pf.TimeData([1, 3, 0], [0, 1, 4])
+    freq_data = pf.FrequencyData([2, 0, 1.5], [10, 100, 1000])
+
+    assert pf.dsp.normalize(signal) == pf.dsp.normalize(signal, domain='time')
+    assert pf.dsp.normalize(time_data) == pf.dsp.normalize(time_data,
+                                                           domain='time')
+    assert pf.dsp.normalize(freq_data) == pf.dsp.normalize(freq_data,
+                                                           domain='freq')
+
+
 @pytest.mark.parametrize('unit, limit1, limit2', (
                          [None, (0, 1000), (1000, 2000)],
                          ['s', (0, 0.5), (0.5, 1)]))
@@ -132,7 +145,8 @@ def test_error_raises():
                                    "'pyfar.classes.audio.TimeData'>'")):
         pf.dsp.normalize(pf.TimeData([1, 1, 1], [1, 2, 3]), domain='freq')
 
-    with raises(ValueError, match=("domain must be 'time' or 'freq'.")):
+    with raises(ValueError, match=("domain must be 'time', 'freq' or 'auto' "
+                                   "but is 'invalid_domain'.")):
         pf.dsp.normalize(pf.Signal([0, 1, 0], 44100), domain='invalid_domain')
 
     with raises(ValueError, match=("reference_method must be 'max', 'mean',")):