Source code for annotations.FillIPUs.fillipus

# -*- coding: UTF-8 -*-
"""
:filename: sppas.src.annotations.FillIPUs.fillipus.py
:author:   Brigitte Bigi
:contact:  develop@sppas.org
:summary:  Search for IPUS and fill in IPUs with a given transcription.

.. _This file is part of SPPAS: http://www.sppas.org/
..
    -------------------------------------------------------------------------

     ___   __    __    __    ___
    /     |  \  |  \  |  \  /              the automatic
    \__   |__/  |__/  |___| \__             annotation and
       \  |     |     |   |    \             analysis
    ___/  |     |     |   | ___/              of speech

    Copyright (C) 2011-2021  Brigitte Bigi
    Laboratoire Parole et Langage, Aix-en-Provence, France

    Use of this software is governed by the GNU Public License, version 3.

    SPPAS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    SPPAS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with SPPAS. If not, see <http://www.gnu.org/licenses/>.

    This banner notice must not be removed.

    -------------------------------------------------------------------------

"""

from sppas.src.config import symbols

from ..SearchIPUs.searchipus import SearchIPUs

# ---------------------------------------------------------------------------

SIL_ORTHO = list(symbols.ortho.keys())[list(symbols.ortho.values()).index("silence")]

# ---------------------------------------------------------------------------


[docs]class FillIPUs(SearchIPUs): """Search for IPUs and fill in the IPUs with a transcription. """
[docs] def __init__(self, channel, units): """Instantiate. :param channel: (sppasChannel) :param units: (list of str) All units to fill. The given units can be either ipus+silences or ipus only. """ super(FillIPUs, self).__init__(channel) self._units = units self._nb_ipus = len([u for u in self._units if u != SIL_ORTHO])
# ----------------------------------------------------------------------- def __check_boundaries(self, tracks): """Check if silences at start and end are as expected. :param tracks: :returns: (bool) """ if len(self._units) == 0: return False if self._channel is None: return False first_from_pos = tracks[0][0] last_to_pos = tracks[-1][1] # If I expected a silence at start... and I've found a track if self._units[0] == SIL_ORTHO and \ first_from_pos < 10: return False # If I expected a silence at end... and I've found a track if self._units[-1] == SIL_ORTHO and \ last_to_pos > (self._channel.get_nframes()-10): return False return True # ----------------------------------------------------------------------- def _split_into_vol(self): """Try various volume values when estimating silences. This method expect to find the right number of ipus and silences. It automatically adjusts the member vol_threshold. :returns: number of ipus really found """ if self._channel is None: return 0 vol_stats = self.get_volstats() # Min volume in the recorded channel vmin = vol_stats.min() # Max is set to the mean vmax = vol_stats.mean() # A volume step is necessary to not exaggerate a detailed search! # vstep is set to 5% of the volume between min and mean. vstep = int((vmax - vmin) / 20.0) # Min and max are adjusted vmin += vstep vmax -= vstep # First Test self._vol_threshold = vmin tracks = self.get_tracks(vmin) n = len(tracks) b = self.__check_boundaries(tracks) while n != self._nb_ipus or b is False: # We would never be done anyway. if (vmax == vmin) or (vmax-vmin) < vstep: return n # Try with the middle volume value vmid = int(vmin + (vmax - vmin) / 2.0) if n > self._nb_ipus: # We split too often. Need to consider less as silence. vmax = vmid elif n < self._nb_ipus: # We split too seldom. Need to consider more as silence. vmin = vmid else: # We did not find start/end silence. vmin += vstep # Find silences with these parameters self._vol_threshold = int(vmid) tracks = self.get_tracks(vmid) n = len(tracks) b = self.__check_boundaries(tracks) return n # -----------------------------------------------------------------------
[docs] def fix_threshold_durations(self): """Search appropriate parameters to match the units and the channel. Try various volume values, pause durations and silence duration to search silences then tracks. Stops when the number of tracks automatically found is matching the number of given units. :returns: tracks """ if self._channel is None: raise Exception('No audio data.') # Search tracks with default parameters self._vol_threshold = self.fix_threshold_vol() tracks = self.get_tracks(self._vol_threshold) n = len(tracks) b = self.__check_boundaries(tracks) if n == self._nb_ipus and b is True: return n # Search by changing only the volume threshold n = self._split_into_vol() if n > self._nb_ipus: # We split too often. # Search with higher minimum duration of silences and ipus while n > self._nb_ipus: self._min_sil_dur += self._win_length self._min_ipu_dur += self._win_length n = self._split_into_vol() elif n < self._nb_ipus: # We split too seldom. # Search with smaller minimum duration of silences and ipus p = self._min_sil_dur m = self._min_ipu_dur while n < self._nb_ipus and \ self._min_sil_dur > SearchIPUs.MIN_SIL_DUR: self._min_sil_dur -= self._win_length n = self._split_into_vol() # we failed... try with shorter' values of ipus if n < self._nb_ipus: self._min_sil_dur = p while n < self._nb_ipus and \ self._min_ipu_dur > SearchIPUs.MIN_IPU_DUR: self._min_ipu_dur -= self._win_length n = self._split_into_vol() # we failed... try with shorter' values of both sil/ipus if n < self._nb_ipus: self._min_ipu_dur = m while n < self._nb_ipus and \ self._min_sil_dur > SearchIPUs.MIN_SIL_DUR and \ self._min_ipu_dur > SearchIPUs.MIN_IPU_DUR: self._min_ipu_dur -= self._win_length self._min_sil_dur -= self._win_length n = self._split_into_vol() return n