changeset 402:954c1e279068

Remove deprecated code from util.py
author Amine Sehili <amine.sehili@gmail.com>
date Sun, 26 May 2024 17:19:31 +0200
parents b11c51a0eade
children 996948ada980
files auditok/exceptions.py auditok/util.py
diffstat 2 files changed, 30 insertions(+), 352 deletions(-) [+]
line wrap: on
line diff
--- a/auditok/exceptions.py	Sat May 25 22:48:25 2024 +0200
+++ b/auditok/exceptions.py	Sun May 26 17:19:31 2024 +0200
@@ -1,14 +1,10 @@
-class DuplicateArgument(Exception):
-    pass
-
-
-class TooSamllBlockDuration(ValueError):
+class TooSmallBlockDuration(ValueError):
     """Raised when block_dur results in a block_size smaller than one sample."""
 
     def __init__(self, message, block_dur, sampling_rate):
         self.block_dur = block_dur
         self.sampling_rate = sampling_rate
-        super(TooSamllBlockDuration, self).__init__(message)
+        super(TooSmallBlockDuration, self).__init__(message)
 
 
 class TimeFormatError(Exception):
--- a/auditok/util.py	Sat May 25 22:48:25 2024 +0200
+++ b/auditok/util.py	Sun May 26 17:19:31 2024 +0200
@@ -8,22 +8,20 @@
     make_duration_formatter
     make_channel_selector
 """
+
+import warnings
 from abc import ABC, abstractmethod
-import warnings
 from functools import partial
+
+from .exceptions import TimeFormatError, TooSmallBlockDuration
 from .io import (
     AudioIOError,
     AudioSource,
-    from_file,
     BufferAudioSource,
     PyAudioSource,
+    from_file,
     get_audio_source,
 )
-from .exceptions import (
-    DuplicateArgument,
-    TooSamllBlockDuration,
-    TimeFormatError,
-)
 
 try:
     from . import signal_numpy as signal
@@ -37,8 +35,6 @@
     "DataSource",
     "DataValidator",
     "StringDataSource",
-    "ADSFactory",
-    "AudioDataSource",
     "AudioReader",
     "Recorder",
     "AudioEnergyValidator",
@@ -108,12 +104,12 @@
     """
     if fmt == "%S":
 
-        def fromatter(seconds):
+        def formatter(seconds):
             return "{:.3f}".format(seconds)
 
     elif fmt == "%I":
 
-        def fromatter(seconds):
+        def formatter(seconds):
             return "{0}".format(int(seconds * 1000))
 
     else:
@@ -129,14 +125,14 @@
         except ValueError:
             pass
 
-        def fromatter(seconds):
+        def formatter(seconds):
             millis = int(seconds * 1000)
             hrs, millis = divmod(millis, 3600000)
             mins, millis = divmod(millis, 60000)
             secs, millis = divmod(millis, 1000)
             return fmt.format(hrs=hrs, mins=mins, secs=secs, millis=millis)
 
-    return fromatter
+    return formatter
 
 
 def make_channel_selector(sample_width, channels, selected=None):
@@ -374,317 +370,6 @@
         self._current = 0
 
 
-class ADSFactory:
-    """
-    .. deprecated:: 2.0.0
-          `ADSFactory` will be removed in auditok 2.0.1, use instances of
-          :class:`AudioReader` instead.
-
-    Factory class that makes it easy to create an
-    :class:`AudioDataSource` object that implements
-    :class:`DataSource` and can therefore be passed to
-    :func:`auditok.core.StreamTokenizer.tokenize`.
-
-    Whether you read audio data from a file, the microphone or a memory buffer,
-    this factory instantiates and returns the right
-    :class:`AudioDataSource` object.
-
-    There are many other features you want a :class:`AudioDataSource` object to
-    have, such as: memorize all read audio data so that you can rewind and reuse
-    it (especially useful when reading data from the microphone), read a fixed
-    amount of data (also useful when reading from the microphone), read
-    overlapping audio frames (often needed when dosing a spectral analysis of
-    data).
-
-    :func:`ADSFactory.ads` automatically creates and return object with the
-    desired behavior according to the supplied keyword arguments.
-    """
-
-    @staticmethod  # noqa: C901
-    def _check_normalize_args(kwargs):
-
-        for k in kwargs:
-            if k not in [
-                "block_dur",
-                "hop_dur",
-                "block_size",
-                "hop_size",
-                "max_time",
-                "record",
-                "audio_source",
-                "filename",
-                "data_buffer",
-                "frames_per_buffer",
-                "sampling_rate",
-                "sample_width",
-                "channels",
-                "sr",
-                "sw",
-                "ch",
-                "asrc",
-                "fn",
-                "fpb",
-                "db",
-                "mt",
-                "rec",
-                "bd",
-                "hd",
-                "bs",
-                "hs",
-            ]:
-                raise ValueError("Invalid argument: {0}".format(k))
-
-        if "block_dur" in kwargs and "bd" in kwargs:
-            raise DuplicateArgument(
-                "Either 'block_dur' or 'bd' must be specified, not both"
-            )
-
-        if "hop_dur" in kwargs and "hd" in kwargs:
-            raise DuplicateArgument(
-                "Either 'hop_dur' or 'hd' must be specified, not both"
-            )
-
-        if "block_size" in kwargs and "bs" in kwargs:
-            raise DuplicateArgument(
-                "Either 'block_size' or 'bs' must be specified, not both"
-            )
-
-        if "hop_size" in kwargs and "hs" in kwargs:
-            raise DuplicateArgument(
-                "Either 'hop_size' or 'hs' must be specified, not both"
-            )
-
-        if "max_time" in kwargs and "mt" in kwargs:
-            raise DuplicateArgument(
-                "Either 'max_time' or 'mt' must be specified, not both"
-            )
-
-        if "audio_source" in kwargs and "asrc" in kwargs:
-            raise DuplicateArgument(
-                "Either 'audio_source' or 'asrc' must be specified, not both"
-            )
-
-        if "filename" in kwargs and "fn" in kwargs:
-            raise DuplicateArgument(
-                "Either 'filename' or 'fn' must be specified, not both"
-            )
-
-        if "data_buffer" in kwargs and "db" in kwargs:
-            raise DuplicateArgument(
-                "Either 'filename' or 'db' must be specified, not both"
-            )
-
-        if "frames_per_buffer" in kwargs and "fbb" in kwargs:
-            raise DuplicateArgument(
-                "Either 'frames_per_buffer' or 'fpb' must be specified, not "
-                "both"
-            )
-
-        if "sampling_rate" in kwargs and "sr" in kwargs:
-            raise DuplicateArgument(
-                "Either 'sampling_rate' or 'sr' must be specified, not both"
-            )
-
-        if "sample_width" in kwargs and "sw" in kwargs:
-            raise DuplicateArgument(
-                "Either 'sample_width' or 'sw' must be specified, not both"
-            )
-
-        if "channels" in kwargs and "ch" in kwargs:
-            raise DuplicateArgument(
-                "Either 'channels' or 'ch' must be specified, not both"
-            )
-
-        if "record" in kwargs and "rec" in kwargs:
-            raise DuplicateArgument(
-                "Either 'record' or 'rec' must be specified, not both"
-            )
-
-        kwargs["bd"] = kwargs.pop("block_dur", None) or kwargs.pop("bd", None)
-        kwargs["hd"] = kwargs.pop("hop_dur", None) or kwargs.pop("hd", None)
-        kwargs["bs"] = kwargs.pop("block_size", None) or kwargs.pop("bs", None)
-        kwargs["hs"] = kwargs.pop("hop_size", None) or kwargs.pop("hs", None)
-        kwargs["mt"] = kwargs.pop("max_time", None) or kwargs.pop("mt", None)
-        kwargs["asrc"] = kwargs.pop("audio_source", None) or kwargs.pop(
-            "asrc", None
-        )
-        kwargs["fn"] = kwargs.pop("filename", None) or kwargs.pop("fn", None)
-        kwargs["db"] = kwargs.pop("data_buffer", None) or kwargs.pop("db", None)
-
-        record = kwargs.pop("record", False)
-        if not record:
-            record = kwargs.pop("rec", False)
-            if not isinstance(record, bool):
-                raise TypeError("'record' must be a boolean")
-
-        kwargs["rec"] = record
-
-        # keep long names for arguments meant for BufferAudioSource
-        # and PyAudioSource
-        if "frames_per_buffer" in kwargs or "fpb" in kwargs:
-            kwargs["frames_per_buffer"] = kwargs.pop(
-                "frames_per_buffer", None
-            ) or kwargs.pop("fpb", None)
-
-        if "sampling_rate" in kwargs or "sr" in kwargs:
-            kwargs["sampling_rate"] = kwargs.pop(
-                "sampling_rate", None
-            ) or kwargs.pop("sr", None)
-
-        if "sample_width" in kwargs or "sw" in kwargs:
-            kwargs["sample_width"] = kwargs.pop(
-                "sample_width", None
-            ) or kwargs.pop("sw", None)
-
-        if "channels" in kwargs or "ch" in kwargs:
-            kwargs["channels"] = kwargs.pop("channels", None) or kwargs.pop(
-                "ch", None
-            )
-
-    @staticmethod
-    def ads(**kwargs):
-        """
-        Create an return an :class:`AudioDataSource`. The type and
-        behavior of the object is the result
-        of the supplied parameters. Called without any parameters, the class
-        will read audio data from the available built-in microphone with the
-        default parameters.
-
-        Parameters
-        ----------
-        sampling_rate, sr : int, default: 16000
-            number of audio samples per second of input audio stream.
-        sample_width, sw : int, default: 2
-            number of bytes per sample, must be one of 1, 2 or 4
-        channels, ch : int, default: 1
-            number of audio channels, only a value of 1 is currently accepted.
-        frames_per_buffer, fpb : int, default: 1024
-            number of samples of PyAudio buffer.
-        audio_source, asrc : `AudioSource`
-            `AudioSource` to read data from
-        filename, fn : str
-            create an `AudioSource` object using this file
-        data_buffer, db : str
-            build an `io.BufferAudioSource` using data in `data_buffer`.
-            If this keyword is used,
-            `sampling_rate`, `sample_width` and `channels` are passed to
-            `io.BufferAudioSource` constructor and used instead of default
-            values.
-        max_time, mt : float
-            maximum time (in seconds) to read. Default behavior: read until
-            there is no more data
-            available.
-        record, rec : bool, default = False
-            save all read data in cache. Provide a navigable object which has a
-            `rewind` method.
-        block_dur, bd : float
-            processing block duration in seconds. This represents the quantity
-            of audio data to return each time the :func:`read` method is
-            invoked. If `block_dur` is 0.025 (i.e. 25 ms) and the sampling rate
-            is 8000 and the sample width is 2 bytes, :func:`read` returns a
-            buffer of 0.025 * 8000 * 2 = 400 bytes at most. This parameter will
-            be looked for (and used if available) before `block_size`. If
-            neither parameter is given, `block_dur` will be set to 0.01 second
-            (i.e. 10 ms)
-        hop_dur, hd : float
-            quantity of data to skip from current processing window. if
-            `hop_dur` is supplied then there will be an overlap of `block_dur`
-            - `hop_dur` between two adjacent blocks. This parameter will be
-            looked for (and used if available) before `hop_size`.
-            If neither parameter is given, `hop_dur` will be set to `block_dur`
-            which means that there will be no overlap between two consecutively
-            read blocks.
-        block_size, bs : int
-            number of samples to read each time the `read` method is called.
-            Default: a block size that represents a window of 10ms, so for a
-            sampling rate of 16000, the default `block_size` is 160 samples,
-            for a rate of 44100, `block_size` = 441 samples, etc.
-        hop_size, hs : int
-            determines the number of overlapping samples between two adjacent
-            read windows. For a `hop_size` of value *N*, the overlap is
-            `block_size` - *N*. Default : `hop_size` = `block_size`, means that
-            there is no overlap.
-
-        Returns
-        -------
-        audio_data_source : AudioDataSource
-            an `AudioDataSource` object build with input parameters.
-        """
-        warnings.warn(
-            "'ADSFactory' is deprecated and will be removed in a future "
-            "release. Please use AudioReader class instead.",
-            DeprecationWarning,
-        )
-
-        # check and normalize keyword arguments
-        ADSFactory._check_normalize_args(kwargs)
-
-        block_dur = kwargs.pop("bd")
-        hop_dur = kwargs.pop("hd")
-        block_size = kwargs.pop("bs")
-        hop_size = kwargs.pop("hs")
-        max_time = kwargs.pop("mt")
-        audio_source = kwargs.pop("asrc")
-        filename = kwargs.pop("fn")
-        data_buffer = kwargs.pop("db")
-        record = kwargs.pop("rec")
-
-        # Case 1: an audio source is supplied
-        if audio_source is not None:
-            if (filename, data_buffer) != (None, None):
-                raise Warning(
-                    "You should provide one of 'audio_source', 'filename' or \
-                    'data_buffer' keyword parameters. 'audio_source' will be \
-                    used"
-                )
-
-        # Case 2: a file name is supplied
-        elif filename is not None:
-            if data_buffer is not None:
-                raise Warning(
-                    "You should provide one of 'filename' or 'data_buffer'\
-                 keyword parameters. 'filename' will be used"
-                )
-            audio_source = from_file(filename)
-
-        # Case 3: a data_buffer is supplied
-        elif data_buffer is not None:
-            audio_source = BufferAudioSource(data=data_buffer, **kwargs)
-
-        # Case 4: try to access native audio input
-        else:
-            audio_source = PyAudioSource(**kwargs)
-
-        if block_dur is not None:
-            if block_size is not None:
-                raise DuplicateArgument(
-                    "Either 'block_dur' or 'block_size' can be specified, not \
-                    both"
-                )
-        elif block_size is not None:
-            block_dur = block_size / audio_source.sr
-        else:
-            block_dur = 0.01  # 10 ms
-
-        # Read overlapping blocks of data
-        if hop_dur is not None:
-            if hop_size is not None:
-                raise DuplicateArgument(
-                    "Either 'hop_dur' or 'hop_size' can be specified, not both"
-                )
-        elif hop_size is not None:
-            hop_dur = hop_size / audio_source.sr
-
-        ads = AudioDataSource(
-            audio_source,
-            block_dur=block_dur,
-            hop_dur=hop_dur,
-            record=record,
-            max_read=max_time,
-        )
-        return ads
-
-
 class _AudioReadingProxy:
     def __init__(self, audio_source):
 
@@ -817,7 +502,7 @@
     """
 
     def __init__(self, audio_source, block_dur):
-        super(_FixedSizeAudioReader, self).__init__(audio_source)
+        super().__init__(audio_source)
 
         if block_dur <= 0:
             raise ValueError(
@@ -829,7 +514,7 @@
             err_msg = "Too small block_dur ({0:f}) for sampling rate ({1}). "
             err_msg += "block_dur should cover at least one sample "
             err_msg += "(i.e. 1/{1})"
-            raise TooSamllBlockDuration(
+            raise TooSmallBlockDuration(
                 err_msg.format(block_dur, self.sr), block_dur, self.sr
             )
 
@@ -857,7 +542,7 @@
     def __init__(self, audio_source, block_dur, hop_dur):
 
         if hop_dur >= block_dur:
-            raise ValueError('"hop_dur" should be < "block_dur"')
+            raise ValueError('"hop_dur" should be <= "block_dur"')
 
         super(_OverlapAudioReader, self).__init__(audio_source, block_dur)
 
@@ -928,20 +613,20 @@
     ----------
     input : str, bytes, AudioSource, AudioReader, AudioRegion or None
         input audio data. If the type of the passed argument is `str`, it should
-        be a path to an existing audio file. "-" is interpreted as standardinput.
+        be a path to an existing audio file. "-" is interpreted as standard input.
         If the type is `bytes`, input is considered as a buffer of raw audio
         data. If None, read audio from microphone. Every object that is not an
         :class:`AudioReader` will be transformed, when possible, into an
         :class:`AudioSource` before processing. If it is an `str` that refers to
         a raw audio file, `bytes` or None, audio parameters should be provided
-        using kwargs (i.e., `samplig_rate`, `sample_width` and `channels` or
-        their alias).
+        using kwargs (i.e., `sampling_rate`, `sample_width` and `channels` or
+        their alias: `sr`, `sw` and `ch`).
     block_dur: float, default: 0.01
-        length in seconds of audio windows to return at each `read` call.
+        length in seconds of audio data to return for each `read` call.
     hop_dur: float, default: None
         length in seconds of data amount to skip from previous window. If
         defined, it is used to compute the temporal overlap between previous and
-        current window (nameply `overlap = block_dur - hop_dur`). Default, None,
+        current window (namely `overlap = block_dur - hop_dur`). Default, None,
         means that consecutive windows do not overlap.
     record: bool, default: False
         whether to record read audio data for later access. If True, audio data
@@ -955,7 +640,7 @@
         from microphone a Ctrl-C is sent.
 
     When `input` is None, of type bytes or a raw audio files some of the
-    follwing kwargs are mandatory.
+    following kwargs are mandatory.
 
     Other Parameters
     ----------------
@@ -1002,7 +687,7 @@
         hop_dur=None,
         record=False,
         max_read=None,
-        **kwargs
+        **kwargs,
     ):
         if not isinstance(input, AudioSource):
             input = get_audio_source(input, **kwargs)
@@ -1012,10 +697,12 @@
         if max_read is not None:
             input = _Limiter(input, max_read)
             self._max_read = max_read
-        if hop_dur is not None:
+        # TODO: warning if block_dur and hop_dur yield the same size in terms of nb samples
+        if hop_dur is None or hop_dur == block_dur:
+            input = _FixedSizeAudioReader(input, block_dur)
+        else:
             input = _OverlapAudioReader(input, block_dur, hop_dur)
-        else:
-            input = _FixedSizeAudioReader(input, block_dur)
+
         self._audio_source = input
 
     def __repr__(self):
@@ -1075,15 +762,10 @@
             )
         try:
             return getattr(self._audio_source, name)
-        except AttributeError:
+        except AttributeError as exc:
             raise AttributeError(
-                "'AudioReader' has no attribute '{}'".format(name)
-            )
-
-
-# Keep AudioDataSource for compatibility
-# Remove in a future version when ADSFactory is removed
-AudioDataSource = AudioReader
+                f"'AudioReader' has no attribute {name!r}"
+            ) from exc
 
 
 class Recorder(AudioReader):
@@ -1106,5 +788,5 @@
             hop_dur=hop_dur,
             record=True,
             max_read=max_read,
-            **kwargs
+            **kwargs,
         )