From c893e31e371ec09e37cb9522a1bd867c5a4bbbed Mon Sep 17 00:00:00 2001
From: ChristianGeng <christian.c.geng@gmail.com>
Date: Tue, 12 Nov 2024 10:46:10 +0100
Subject: [PATCH 1/4] Add requires line to pyproject.toml.

---
 pyproject.toml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/pyproject.toml b/pyproject.toml
index 5d4005e..e4cd41b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -33,6 +33,7 @@ classifiers = [
     'Programming Language :: Python :: 3.9',
     'Topic :: Scientific/Engineering',
 ]
+requires-python = '>=3.9'
 dependencies = [
     'audeer >=2.0.0',
     'audformat >=1.1.0',

From 3f2b3101f68660ebe3588cdcc61f15aed7f48c78 Mon Sep 17 00:00:00 2001
From: ChristianGeng <christian.c.geng@gmail.com>
Date: Tue, 12 Nov 2024 12:17:01 +0100
Subject: [PATCH 2/4] Remove typing module in remaining files.

---
 auglib/core/interface.py |  16 +-
 auglib/core/observe.py   |  18 ++-
 auglib/core/resolver.py  |  18 +--
 auglib/core/time.py      |   4 +-
 auglib/core/transform.py | 319 +++++++++++++++++++--------------------
 auglib/core/utils.py     |  10 +-
 6 files changed, 194 insertions(+), 191 deletions(-)

diff --git a/auglib/core/interface.py b/auglib/core/interface.py
index 113765b..976bd74 100644
--- a/auglib/core/interface.py
+++ b/auglib/core/interface.py
@@ -1,5 +1,7 @@
+from __future__ import annotations
+
+import collections
 import os
-import typing
 
 import numpy as np
 import pandas as pd
@@ -140,10 +142,10 @@ def __init__(
         *,
         sampling_rate: int = None,
         resample: bool = False,
-        channels: typing.Union[int, typing.Sequence[int]] = None,
+        channels: int | collections.abc.Sequence[int] = None,
         mixdown: bool = False,
         keep_nat: bool = False,
-        num_workers: typing.Optional[int] = 1,
+        num_workers: int | None = 1,
         multiprocessing: bool = False,
         seed: int = None,
         verbose: bool = False,
@@ -184,7 +186,7 @@ def short_id(
 
     def augment(
         self,
-        data: typing.Union[pd.Index, pd.Series, pd.DataFrame],
+        data: pd.Index | pd.Series | pd.DataFrame,
         cache_root: str = None,
         *,
         data_root: str = None,
@@ -192,7 +194,7 @@ def augment(
         modified_only: bool = True,
         num_variants: int = 1,
         force: bool = False,
-    ) -> typing.Union[pd.Index, pd.Series, pd.DataFrame]:
+    ) -> pd.Index | pd.Series | pd.DataFrame:
         r"""Augment an index, column, or table conform to audformat.
 
         Creates ``num_variants`` copies of the segments referenced in the index
@@ -505,10 +507,10 @@ def _apply_nat_mask(
 
 
 def _augmented_files(
-    files: typing.Sequence[str],
+    files: collections.abc.Sequence[str],
     cache_root: str,
     remove_root: str = None,
-) -> typing.List[str]:
+) -> list[str]:
     r"""Return destination path for augmented files.
 
     If files contain the same filename several times,
diff --git a/auglib/core/observe.py b/auglib/core/observe.py
index e018e0e..6076366 100644
--- a/auglib/core/observe.py
+++ b/auglib/core/observe.py
@@ -1,5 +1,7 @@
+from __future__ import annotations
+
+import collections
 import random
-import typing
 
 import numpy as np
 
@@ -13,7 +15,7 @@ class Base(audobject.Object):
 
     """
 
-    def __call__(self) -> typing.Any:  # pragma: no cover
+    def __call__(self) -> object:  # pragma: no cover
         r"""Observe next value.
 
         Returns:
@@ -227,7 +229,7 @@ class List(Base):
     )
     def __init__(
         self,
-        elements: typing.MutableSequence[typing.Any],
+        elements: collections.abc.MutableSequence[object],
         *,
         shuffle: bool = False,
         draw: bool = False,
@@ -241,10 +243,10 @@ def __init__(
         self._counter = 0
         self._iter = False
 
-    def _draw(self) -> typing.Any:
+    def _draw(self) -> object:
         return self.elements[random.randint(0, len(self) - 1)]
 
-    def _next(self) -> typing.Any:
+    def _next(self) -> object:
         if self.shuffle and self._counter == 0:
             random.shuffle(self.elements)
         element = self.elements[self._counter]
@@ -254,7 +256,7 @@ def _next(self) -> typing.Any:
             self._iter = False
         return element
 
-    def __call__(self) -> typing.Any:
+    def __call__(self) -> object:
         r"""Observe next value from list.
 
         Returns:
@@ -281,8 +283,8 @@ def __next__(self):  # noqa: D105
 
 
 def observe(
-    x: typing.Union[typing.Any, Base],
-) -> typing.Any:
+    x: object | Base,
+) -> object:
     r"""Convenient function to observe a value.
 
     Returns ``x()`` if ``x`` is of type :class:`auglib.observe.Base`,
diff --git a/auglib/core/resolver.py b/auglib/core/resolver.py
index 5edc442..6829159 100644
--- a/auglib/core/resolver.py
+++ b/auglib/core/resolver.py
@@ -1,4 +1,4 @@
-import typing
+import collections
 
 import numpy as np
 
@@ -10,14 +10,14 @@ class ArrayResolver(audobject.resolver.Base):
 
     def decode(
         self,
-        value: typing.Any,
-    ) -> typing.Any:
+        value: object,
+    ) -> object:
         return value
 
     def encode(
         self,
-        value: typing.Any,
-    ) -> typing.Any:
+        value: object,
+    ) -> object:
         if isinstance(value, np.ndarray):
             raise ValueError(
                 f"Cannot serialize an instance of "
@@ -37,14 +37,14 @@ class ObservableListResolver(audobject.resolver.Base):
 
     def decode(
         self,
-        value: typing.Any,
-    ) -> typing.Any:
+        value: object,
+    ) -> object:
         return value
 
     def encode(
         self,
-        value: typing.MutableSequence[typing.Any],
-    ) -> typing.Any:
+        value: collections.abc.MutableSequence[object],
+    ) -> object:
         for v in value:
             if isinstance(v, np.ndarray):
                 raise ValueError(
diff --git a/auglib/core/time.py b/auglib/core/time.py
index 675c18c..8fcb82d 100644
--- a/auglib/core/time.py
+++ b/auglib/core/time.py
@@ -1,4 +1,4 @@
-import typing
+from __future__ import annotations
 
 import pandas as pd
 
@@ -43,7 +43,7 @@ class Time(audobject.Object):
 
     def __init__(
         self,
-        value: typing.Union[int, float, observe.Base],
+        value: int | float | observe.Base,
         unit: str,
     ):
         self.value = value
diff --git a/auglib/core/transform.py b/auglib/core/transform.py
index 9f85edb..6f2fa2c 100644
--- a/auglib/core/transform.py
+++ b/auglib/core/transform.py
@@ -3,13 +3,12 @@
 # during serialization,
 # they should never be moved to a different file,
 # and the file should never be renamed.
+from __future__ import annotations
 
+import collections
+from collections.abc import Callable
 import math
 import tempfile
-import typing
-from typing import Callable
-from typing import Sequence
-from typing import Union
 import warnings
 
 import numpy as np
@@ -134,12 +133,12 @@ class Base(audobject.Object):
     )
     def __init__(
         self,
-        bypass_prob: Union[float, observe.Base] = None,
+        bypass_prob: float | observe.Base = None,
         *,
         unit: str = "seconds",
-        preserve_level: Union[bool, observe.Base] = False,
-        aux: Union[str, observe.Base, np.ndarray, "Base"] = None,
-        transform: "Base" = None,
+        preserve_level: bool | observe.Base = False,
+        aux: str | observe.Base | np.ndarray | Base = None,
+        transform: Base = None,
         num_repeat: int = None,
     ):
         self.unit = unit
@@ -260,7 +259,7 @@ def __call__(
 
     def to_samples(
         self,
-        value: typing.Union[int, float, observe.Base, Time],
+        value: int | float | observe.Base | Time,
         sampling_rate: int = None,
         *,
         length: int = None,
@@ -340,11 +339,11 @@ class AMRNB(Base):
 
     def __init__(
         self,
-        bit_rate: Union[int, observe.Base],
+        bit_rate: int | observe.Base,
         *,
-        dtx: Union[bool, observe.Base] = False,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        dtx: bool | observe.Base = False,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             bypass_prob=bypass_prob,
@@ -462,14 +461,14 @@ class Append(Base):
 
     def __init__(
         self,
-        aux: Union[str, observe.Base, np.ndarray, Base],
+        aux: str | observe.Base | np.ndarray | Base,
         *,
-        read_pos_aux: Union[int, float, observe.Base, Time] = 0.0,
-        read_dur_aux: Union[int, float, observe.Base, Time] = None,
+        read_pos_aux: int | float | observe.Base | Time = 0.0,
+        read_dur_aux: int | float | observe.Base | Time = None,
         unit: str = "seconds",
         transform: Base = None,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             aux=aux,
@@ -565,12 +564,12 @@ class AppendValue(Base):
 
     def __init__(
         self,
-        duration: Union[int, float, observe.Base, Time],
-        value: Union[float, observe.Base] = 0,
+        duration: int | float | observe.Base | Time,
+        value: float | observe.Base = 0,
         *,
         unit: str = "seconds",
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             unit=unit,
@@ -699,13 +698,13 @@ class BabbleNoise(Base):
     )
     def __init__(
         self,
-        speech: Sequence[Union[str, np.ndarray]],
+        speech: collections.abc.Sequence[str | np.ndarray],
         *,
-        num_speakers: Union[int, observe.Base] = 5,
-        gain_db: Union[float, observe.Base] = 0.0,
-        snr_db: Union[float, observe.Base] = None,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        num_speakers: int | observe.Base = 5,
+        gain_db: float | observe.Base = 0.0,
+        snr_db: float | observe.Base = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             preserve_level=preserve_level,
@@ -834,13 +833,13 @@ class BandPass(Base):
 
     def __init__(
         self,
-        center: Union[float, observe.Base],
-        bandwidth: Union[float, observe.Base],
+        center: float | observe.Base,
+        bandwidth: float | observe.Base,
         *,
-        order: Union[int, observe.Base] = 1,
+        order: int | observe.Base = 1,
         design: str = "butter",
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             preserve_level=preserve_level,
@@ -964,13 +963,13 @@ class BandStop(Base):
 
     def __init__(
         self,
-        center: Union[float, observe.Base],
-        bandwidth: Union[float, observe.Base],
+        center: float | observe.Base,
+        bandwidth: float | observe.Base,
         *,
-        order: Union[int, observe.Base] = 1,
+        order: int | observe.Base = 1,
         design: str = "butter",
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             preserve_level=preserve_level,
@@ -1075,11 +1074,11 @@ class Clip(Base):
     def __init__(
         self,
         *,
-        threshold: Union[float, observe.Base] = 0.0,
-        soft: Union[bool, observe.Base] = False,
-        normalize: Union[bool, observe.Base] = False,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        threshold: float | observe.Base = 0.0,
+        soft: bool | observe.Base = False,
+        normalize: bool | observe.Base = False,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             preserve_level=preserve_level,
@@ -1184,12 +1183,12 @@ class ClipByRatio(Base):
 
     def __init__(
         self,
-        ratio: Union[float, observe.Base],
+        ratio: float | observe.Base,
         *,
-        soft: Union[bool, observe.Base] = False,
-        normalize: Union[bool, observe.Base] = False,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        soft: bool | observe.Base = False,
+        normalize: bool | observe.Base = False,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             preserve_level=preserve_level,
@@ -1363,10 +1362,10 @@ class Compose(Base):
 
     def __init__(
         self,
-        transforms: Sequence[Base],
+        transforms: collections.abc.Sequence[observe.Base],
         *,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             preserve_level=preserve_level,
@@ -1484,16 +1483,16 @@ class CompressDynamicRange(Base):
 
     def __init__(
         self,
-        threshold_db: Union[float, observe.Base],
-        ratio: Union[float, observe.Base],
+        threshold_db: float | observe.Base,
+        ratio: float | observe.Base,
         *,
-        attack_time: Union[float, observe.Base] = 0.01,
-        release_time: Union[float, observe.Base] = 0.02,
-        knee_radius_db: Union[float, observe.Base] = 4.0,
-        makeup_db: Union[None, float, observe.Base] = 0.0,
-        clip: Union[bool, observe.Base] = False,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        attack_time: float | observe.Base = 0.01,
+        release_time: float | observe.Base = 0.02,
+        knee_radius_db: float | observe.Base = 4.0,
+        makeup_db: None | float | observe.Base = 0.0,
+        clip: bool | observe.Base = False,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             preserve_level=preserve_level,
@@ -1697,15 +1696,15 @@ class Fade(Base):
     def __init__(
         self,
         *,
-        in_dur: Union[int, float, observe.Base, Time] = 0.1,
-        out_dur: Union[int, float, observe.Base, Time] = 0.1,
+        in_dur: int | float | observe.Base | Time = 0.1,
+        out_dur: int | float | observe.Base | Time = 0.1,
         in_shape: str = "tukey",
         out_shape: str = "tukey",
         in_db: float = -120,
         out_db: float = -120,
         unit: str = "seconds",
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             unit=unit,
@@ -1883,12 +1882,12 @@ class FFTConvolve(Base):
 
     def __init__(
         self,
-        aux: Union[str, observe.Base, np.ndarray, Base],
+        aux: str | observe.Base | np.ndarray | Base,
         *,
-        keep_tail: Union[bool, observe.Base] = True,
+        keep_tail: bool | observe.Base = True,
         transform: Base = None,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             aux=aux,
@@ -2035,10 +2034,10 @@ def plus_1(x, sr):
     def __init__(
         self,
         function: Callable[..., np.ndarray],
-        function_args: typing.Dict = None,
+        function_args: dict = None,
         *,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             preserve_level=preserve_level,
@@ -2125,12 +2124,12 @@ class GainStage(Base):
 
     def __init__(
         self,
-        gain_db: Union[float, observe.Base],
+        gain_db: float | observe.Base,
         *,
-        max_peak_db: Union[float, observe.Base] = None,
-        clip: Union[bool, observe.Base] = False,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        max_peak_db: float | observe.Base = None,
+        clip: bool | observe.Base = False,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             preserve_level=preserve_level,
@@ -2281,12 +2280,12 @@ class HighPass(Base):
 
     def __init__(
         self,
-        cutoff: Union[float, observe.Base],
+        cutoff: float | observe.Base,
         *,
-        order: Union[int, observe.Base] = 1,
+        order: int | observe.Base = 1,
         design: str = "butter",
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             preserve_level=preserve_level,
@@ -2438,12 +2437,12 @@ class LowPass(Base):
 
     def __init__(
         self,
-        cutoff: Union[float, observe.Base],
+        cutoff: float | observe.Base,
         *,
-        order: Union[int, observe.Base] = 1,
+        order: int | observe.Base = 1,
         design: str = "butter",
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             preserve_level=preserve_level,
@@ -2626,22 +2625,22 @@ def __init__(
         self,
         transform: Base,
         *,
-        start_pos: typing.Union[int, float, observe.Base, Time] = 0,
-        duration: typing.Union[int, float, observe.Base, Time] = None,
-        step: typing.Union[
-            int,
-            float,
-            observe.Base,
-            Time,
-            typing.Tuple[
-                Union[int, float, observe.Base, Time],
-                Union[int, float, observe.Base, Time],
-            ],
-        ] = None,
-        invert: typing.Union[bool, observe.Base] = False,
+        start_pos: int | float | observe.Base | Time = 0,
+        duration: int | float | observe.Base | Time = None,
+        step: (
+            int
+            | float
+            | observe.Base
+            | Time
+            | tuple[
+                int | float | observe.Base | Time,
+                int | float | observe.Base | Time,
+            ]
+        ) = None,
+        invert: bool | observe.Base = False,
         unit: str = "seconds",
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: typing.Union[float, observe.Base] = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         if step is not None:
             step = audeer.to_list(step)
@@ -2890,22 +2889,22 @@ class Mix(Base):
 
     def __init__(
         self,
-        aux: Union[str, observe.Base, np.ndarray, Base],
+        aux: str | observe.Base | np.ndarray | Base,
         *,
-        gain_base_db: Union[float, observe.Base] = 0.0,
-        gain_aux_db: Union[float, observe.Base] = 0.0,
-        snr_db: Union[float, observe.Base] = None,
-        write_pos_base: Union[int, float, observe.Base, Time] = 0.0,
-        read_pos_aux: Union[int, float, observe.Base, Time] = 0.0,
-        read_dur_aux: Union[int, float, observe.Base, Time] = None,
-        clip_mix: Union[bool, observe.Base] = False,
-        loop_aux: Union[bool, observe.Base] = False,
-        extend_base: Union[bool, observe.Base] = False,
-        num_repeat: Union[int, observe.Base] = 1,
+        gain_base_db: float | observe.Base = 0.0,
+        gain_aux_db: float | observe.Base = 0.0,
+        snr_db: float | observe.Base = None,
+        write_pos_base: int | float | observe.Base | Time = 0.0,
+        read_pos_aux: int | float | observe.Base | Time = 0.0,
+        read_dur_aux: int | float | observe.Base | Time = None,
+        clip_mix: bool | observe.Base = False,
+        loop_aux: bool | observe.Base = False,
+        extend_base: bool | observe.Base = False,
+        num_repeat: int | observe.Base = 1,
         unit: str = "seconds",
         transform: Base = None,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             aux=aux,
@@ -3079,10 +3078,10 @@ class NormalizeByPeak(Base):
     def __init__(
         self,
         *,
-        peak_db: Union[float, observe.Base] = 0.0,
-        clip: Union[bool, observe.Base] = False,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        peak_db: float | observe.Base = 0.0,
+        clip: bool | observe.Base = False,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             preserve_level=preserve_level,
@@ -3218,10 +3217,10 @@ class PinkNoise(Base):
     def __init__(
         self,
         *,
-        gain_db: Union[float, observe.Base] = 0.0,
-        snr_db: Union[float, observe.Base] = None,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        gain_db: float | observe.Base = 0.0,
+        snr_db: float | observe.Base = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             preserve_level=preserve_level,
@@ -3354,14 +3353,14 @@ class Prepend(Base):
 
     def __init__(
         self,
-        aux: Union[str, observe.Base, np.ndarray, Base],
+        aux: str | observe.Base | np.ndarray | Base,
         *,
-        read_pos_aux: Union[int, float, observe.Base, Time] = 0.0,
-        read_dur_aux: Union[int, float, observe.Base, Time] = None,
+        read_pos_aux: int | float | observe.Base | Time = 0.0,
+        read_dur_aux: int | float | observe.Base | Time = None,
         unit: str = "seconds",
         transform: Base = None,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             aux=aux,
@@ -3457,12 +3456,12 @@ class PrependValue(Base):
 
     def __init__(
         self,
-        duration: Union[int, float, observe.Base, Time],
-        value: Union[float, observe.Base] = 0,
+        duration: int | float | observe.Base | Time,
+        value: float | observe.Base = 0,
         *,
         unit: str = "seconds",
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             unit=unit,
@@ -3635,10 +3634,10 @@ class Resample(Base):
 
     def __init__(
         self,
-        target_rate: typing.Union[int, observe.List],
+        target_rate: int | observe.List,
         *,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: typing.Union[float, observe.Base] = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
         **kwargs,
     ):
         super().__init__(
@@ -3730,10 +3729,10 @@ class Select(Base):
 
     def __init__(
         self,
-        transforms: Sequence[Base],
+        transforms: collections.abc.Sequence[Base],
         *,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             preserve_level=preserve_level,
@@ -3809,11 +3808,11 @@ class Shift(Base):
 
     def __init__(
         self,
-        duration: typing.Union[int, float, observe.Base, Time] = None,
+        duration: int | float | observe.Base | Time = None,
         *,
         unit: str = "seconds",
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: typing.Union[float, observe.Base] = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             unit=unit,
@@ -3946,15 +3945,15 @@ class Tone(Base):
 
     def __init__(
         self,
-        freq: Union[float, observe.Base],
+        freq: float | observe.Base,
         *,
-        gain_db: Union[float, observe.Base] = 0.0,
-        snr_db: Union[float, observe.Base] = None,
-        shape: Union[str, observe.Base] = "sine",
-        lfo_rate: Union[float, observe.Base] = 0.0,
-        lfo_range: Union[float, observe.Base] = 0.0,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        gain_db: float | observe.Base = 0.0,
+        snr_db: float | observe.Base = None,
+        shape: str | observe.Base = "sine",
+        lfo_rate: float | observe.Base = 0.0,
+        lfo_range: float | observe.Base = 0.0,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             preserve_level=preserve_level,
@@ -4222,14 +4221,14 @@ class Trim(Base):
     def __init__(
         self,
         *,
-        start_pos: Union[int, float, observe.Base, Time] = 0,
-        end_pos: Union[int, float, observe.Base, Time] = None,
-        duration: Union[int, float, observe.Base, Time] = None,
+        start_pos: int | float | observe.Base | Time = 0,
+        end_pos: int | float | observe.Base | Time = None,
+        duration: int | float | observe.Base | Time = None,
         fill: str = "none",
         fill_pos: str = "right",
         unit: str = "seconds",
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             unit=unit,
@@ -4546,11 +4545,11 @@ class WhiteNoiseGaussian(Base):
     def __init__(
         self,
         *,
-        gain_db: Union[float, observe.Base] = 0.0,
-        snr_db: Union[float, observe.Base] = None,
-        stddev: Union[float, observe.Base] = 0.3,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        gain_db: float | observe.Base = 0.0,
+        snr_db: float | observe.Base = None,
+        stddev: float | observe.Base = 0.3,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             preserve_level=preserve_level,
@@ -4693,10 +4692,10 @@ class WhiteNoiseUniform(Base):
     def __init__(
         self,
         *,
-        gain_db: Union[float, observe.Base] = 0.0,
-        snr_db: Union[float, observe.Base] = None,
-        preserve_level: Union[bool, observe.Base] = False,
-        bypass_prob: Union[float, observe.Base] = None,
+        gain_db: float | observe.Base = 0.0,
+        snr_db: float | observe.Base = None,
+        preserve_level: bool | observe.Base = False,
+        bypass_prob: float | observe.Base = None,
     ):
         super().__init__(
             preserve_level=preserve_level,
diff --git a/auglib/core/utils.py b/auglib/core/utils.py
index 2d486f0..f2a0f29 100644
--- a/auglib/core/utils.py
+++ b/auglib/core/utils.py
@@ -1,4 +1,4 @@
-import typing
+from __future__ import annotations
 
 import numpy as np
 
@@ -6,7 +6,7 @@
 from auglib.core import time
 
 
-def from_db(x_db: typing.Union[float, observe.Base]) -> float:
+def from_db(x_db: float | observe.Base) -> float:
     r"""Convert decibels (dB) to gain.
 
     Args:
@@ -72,7 +72,7 @@ def rms_db(signal: np.ndarray) -> float:
     return float(10 * np.log10(max(1e-12, power)))
 
 
-def to_db(x: typing.Union[float, observe.Base]) -> float:
+def to_db(x: float | observe.Base) -> float:
     r"""Convert gain to decibels (dB).
 
     Args:
@@ -87,13 +87,13 @@ def to_db(x: typing.Union[float, observe.Base]) -> float:
 
     """
     x = observe.observe(x)
-    assert x > 0, "cannot convert gain {} to decibels".format(x)
+    assert x > 0, f"cannot convert gain {x} to decibels"
     x_db = 20 * np.log10(x)
     return float(x_db)
 
 
 def to_samples(
-    value: typing.Union[int, float, observe.Base, time.Time],
+    value: int | float | observe.Base | time.Time,
     *,
     sampling_rate: int = None,
     length: int = None,

From 392780b39a75e5d56f524f0a5dec82ff9875cbd8 Mon Sep 17 00:00:00 2001
From: ChristianGeng <christian.c.geng@gmail.com>
Date: Tue, 12 Nov 2024 12:24:50 +0100
Subject: [PATCH 3/4] Fix bug that refactors two incorrect type.

---
 auglib/core/transform.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/auglib/core/transform.py b/auglib/core/transform.py
index 6f2fa2c..fc9436f 100644
--- a/auglib/core/transform.py
+++ b/auglib/core/transform.py
@@ -1362,7 +1362,7 @@ class Compose(Base):
 
     def __init__(
         self,
-        transforms: collections.abc.Sequence[observe.Base],
+        transforms: collections.abc.Sequence[Base],
         *,
         preserve_level: bool | observe.Base = False,
         bypass_prob: float | observe.Base = None,

From 2e868fc32f1543d01a1f3733c138656fcc6efde0 Mon Sep 17 00:00:00 2001
From: ChristianGeng <christian.c.geng@gmail.com>
Date: Wed, 13 Nov 2024 09:19:49 +0100
Subject: [PATCH 4/4] Change import style for collections.abc.

---
 auglib/core/interface.py | 6 +++---
 auglib/core/observe.py   | 4 ++--
 auglib/core/resolver.py  | 4 ++--
 auglib/core/transform.py | 8 ++++----
 4 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/auglib/core/interface.py b/auglib/core/interface.py
index 976bd74..06b1841 100644
--- a/auglib/core/interface.py
+++ b/auglib/core/interface.py
@@ -1,6 +1,6 @@
 from __future__ import annotations
 
-import collections
+from collections.abc import Sequence
 import os
 
 import numpy as np
@@ -142,7 +142,7 @@ def __init__(
         *,
         sampling_rate: int = None,
         resample: bool = False,
-        channels: int | collections.abc.Sequence[int] = None,
+        channels: int | Sequence[int] = None,
         mixdown: bool = False,
         keep_nat: bool = False,
         num_workers: int | None = 1,
@@ -507,7 +507,7 @@ def _apply_nat_mask(
 
 
 def _augmented_files(
-    files: collections.abc.Sequence[str],
+    files: Sequence[str],
     cache_root: str,
     remove_root: str = None,
 ) -> list[str]:
diff --git a/auglib/core/observe.py b/auglib/core/observe.py
index 6076366..ded4278 100644
--- a/auglib/core/observe.py
+++ b/auglib/core/observe.py
@@ -1,6 +1,6 @@
 from __future__ import annotations
 
-import collections
+from collections.abc import MutableSequence
 import random
 
 import numpy as np
@@ -229,7 +229,7 @@ class List(Base):
     )
     def __init__(
         self,
-        elements: collections.abc.MutableSequence[object],
+        elements: MutableSequence[object],
         *,
         shuffle: bool = False,
         draw: bool = False,
diff --git a/auglib/core/resolver.py b/auglib/core/resolver.py
index 6829159..a554480 100644
--- a/auglib/core/resolver.py
+++ b/auglib/core/resolver.py
@@ -1,4 +1,4 @@
-import collections
+from collections.abc import MutableSequence
 
 import numpy as np
 
@@ -43,7 +43,7 @@ def decode(
 
     def encode(
         self,
-        value: collections.abc.MutableSequence[object],
+        value: MutableSequence[object],
     ) -> object:
         for v in value:
             if isinstance(v, np.ndarray):
diff --git a/auglib/core/transform.py b/auglib/core/transform.py
index fc9436f..18c1380 100644
--- a/auglib/core/transform.py
+++ b/auglib/core/transform.py
@@ -5,8 +5,8 @@
 # and the file should never be renamed.
 from __future__ import annotations
 
-import collections
 from collections.abc import Callable
+from collections.abc import Sequence
 import math
 import tempfile
 import warnings
@@ -698,7 +698,7 @@ class BabbleNoise(Base):
     )
     def __init__(
         self,
-        speech: collections.abc.Sequence[str | np.ndarray],
+        speech: Sequence[str | np.ndarray],
         *,
         num_speakers: int | observe.Base = 5,
         gain_db: float | observe.Base = 0.0,
@@ -1362,7 +1362,7 @@ class Compose(Base):
 
     def __init__(
         self,
-        transforms: collections.abc.Sequence[Base],
+        transforms: Sequence[Base],
         *,
         preserve_level: bool | observe.Base = False,
         bypass_prob: float | observe.Base = None,
@@ -3729,7 +3729,7 @@ class Select(Base):
 
     def __init__(
         self,
-        transforms: collections.abc.Sequence[Base],
+        transforms: Sequence[Base],
         *,
         preserve_level: bool | observe.Base = False,
         bypass_prob: float | observe.Base = None,