Source code for aeneas.syncmap.fragment

#!/usr/bin/env python
# coding=utf-8

# aeneas is a Python/C library and a set of tools
# to automagically synchronize audio and text (aka forced alignment)
#
# Copyright (C) 2012-2013, Alberto Pettarin (www.albertopettarin.it)
# Copyright (C) 2013-2015, ReadBeyond Srl   (www.readbeyond.it)
# Copyright (C) 2015-2017, Alberto Pettarin (www.albertopettarin.it)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from __future__ import absolute_import
from __future__ import print_function

from aeneas.exacttiming import Decimal
from aeneas.exacttiming import TimeInterval
from aeneas.exacttiming import TimeValue
import aeneas.globalfunctions as gf


[docs]class SyncMapFragment(object): """ A sync map fragment, that is, a text fragment and an associated time interval. :param text_fragment: the text fragment :type text_fragment: :class:`~aeneas.textfile.TextFragment` :param begin: the begin time of the audio interval :type begin: :class:`~aeneas.exacttiming.TimeValue` :param end: the end time of the audio interval :type end: :class:`~aeneas.exacttiming.TimeValue` :param float confidence: the confidence of the audio timing """ TAG = u"SyncMapFragment" REGULAR = 0 """ Regular fragment """ HEAD = 1 """ Head fragment """ TAIL = 2 """ Tail fragment """ NONSPEECH = 3 """ Nonspeech fragment (not head nor tail) """ NOT_REGULAR_TYPES = [HEAD, TAIL, NONSPEECH] """ Types of fragment different than ``REGULAR`` """ def __init__( self, text_fragment=None, interval=None, begin=None, end=None, fragment_type=REGULAR, confidence=1.0 ): self.text_fragment = text_fragment if interval is not None: self.interval = interval elif (begin is not None) and (end is not None): self.interval = TimeInterval(begin, end) else: self.interval = None self.fragment_type = fragment_type self.confidence = confidence def __unicode__(self): return u"%s %d %.3f %.3f" % ( self.text_fragment.identifier, self.fragment_type, self.begin, self.end ) def __str__(self): return gf.safe_str(self.__unicode__()) def __eq__(self, other): if not isinstance(other, SyncMapFragment): return False return self.interval == other.interval def __ne__(self, other): return not (self == other) def __gt__(self, other): if not isinstance(other, SyncMapFragment): return False return self.interval > other.interval def __lt__(self, other): if not isinstance(other, SyncMapFragment): return False return self.interval < other.interval def __ge__(self, other): return (self > other) or (self == other) def __le__(self, other): return (self < other) or (self == other) @property def text_fragment(self): """ The text fragment associated with this sync map fragment. :rtype: :class:`~aeneas.textfile.TextFragment` """ return self.__text_fragment @text_fragment.setter def text_fragment(self, text_fragment): self.__text_fragment = text_fragment @property def interval(self): """ The time interval corresponding to this fragment. :rtype: :class:`~aeneas.exacttiming.TimeInterval` """ return self.__interval @interval.setter def interval(self, interval): self.__interval = interval @property def fragment_type(self): """ The type of fragment. Possible values are: * :data:`~aeneas.syncmap.fragment.SyncMapFragment.REGULAR` * :data:`~aeneas.syncmap.fragment.SyncMapFragment.HEAD` * :data:`~aeneas.syncmap.fragment.SyncMapFragment.TAIL` * :data:`~aeneas.syncmap.fragment.SyncMapFragment.NONSPEECH` :rtype: int """ return self.__fragment_type @fragment_type.setter def fragment_type(self, fragment_type): self.__fragment_type = fragment_type @property def is_head_or_tail(self): """ Return ``True`` if the fragment is HEAD or TAIL. :rtype: bool .. versionadded:: 1.7.0 """ return self.fragment_type in [self.HEAD, self.TAIL] @property def is_regular(self): """ Return ``True`` if the fragment is REGULAR. :rtype: bool .. versionadded:: 1.7.0 """ return self.fragment_type == self.REGULAR @property def confidence(self): """ The confidence of the audio timing, from ``0.0`` to ``1.0``. Currently this value is not used, and it is always ``1.0``. :rtype: float """ return self.__confidence @confidence.setter def confidence(self, confidence): self.__confidence = confidence @property def pretty_print(self): """ Pretty print representation of this fragment, as ``(identifier, begin, end, text)``. :rtype: string .. versionadded:: 1.7.0 """ return u"%s\t%.3f\t%.3f\t%s" % ( (self.identifier or u""), (self.begin if self.begin is not None else TimeValue("-2.000")), (self.end if self.end is not None else TimeValue("-1.000")), (self.text or u"") ) @property def identifier(self): """ The identifier of this sync map fragment. :rtype: string .. versionadded:: 1.7.0 """ if self.text_fragment is None: return None return self.text_fragment.identifier @property def text(self): """ The text of this sync map fragment. :rtype: string .. versionadded:: 1.7.0 """ if self.text_fragment is None: return None return self.text_fragment.text @property def begin(self): """ The begin time of this sync map fragment. :rtype: :class:`~aeneas.exacttiming.TimeValue` """ if self.interval is None: return None return self.interval.begin @begin.setter def begin(self, begin): if self.interval is None: raise TypeError(u"Attempting to set begin when interval is None") if not isinstance(begin, TimeValue): raise TypeError(u"The given begin value is not an instance of TimeValue") self.interval.begin = begin @property def end(self): """ The end time of this sync map fragment. :rtype: :class:`~aeneas.exacttiming.TimeValue` """ if self.interval is None: return None return self.interval.end @end.setter def end(self, end): if self.interval is None: raise TypeError(u"Attempting to set end when interval is None") if not isinstance(end, TimeValue): raise TypeError(u"The given end value is not an instance of TimeValue") self.interval.end = end @property def length(self): """ The audio duration of this sync map fragment, as end time minus begin time. :rtype: :class:`~aeneas.exacttiming.TimeValue` """ if self.interval is None: return TimeValue("0.000") return self.interval.length @property def has_zero_length(self): """ Returns ``True`` if this sync map fragment has zero length, that is, if its begin and end values coincide. :rtype: bool .. versionadded:: 1.7.0 """ return self.length == TimeValue("0.000") @property def chars(self): """ Return the number of characters of the text fragment, not including the line separators. :rtype: int .. versionadded:: 1.2.0 """ if self.text_fragment is None: return 0 return self.text_fragment.chars @property def rate(self): """ The rate, in characters/second, of this fragment. If the fragment is not ``REGULAR`` or its duration is zero, return ``None``. :rtype: ``None`` or :class:`~aeneas.exacttiming.Decimal` .. versionadded:: 1.2.0 """ if ( (self.fragment_type != self.REGULAR) or (self.has_zero_length) ): return None return Decimal(self.chars / self.length)
[docs] def rate_lack(self, max_rate): """ The time interval that this fragment lacks to respect the given max rate. A positive value means that the current fragment is faster than the max rate (bad). A negative or zero value means that the current fragment has rate slower or equal to the max rate (good). Always return ``0.000`` for fragments that are not ``REGULAR``. :param max_rate: the maximum rate (characters/second) :type max_rate: :class:`~aeneas.exacttiming.Decimal` :rtype: :class:`~aeneas.exacttiming.TimeValue` .. versionadded:: 1.7.0 """ if self.fragment_type == self.REGULAR: return self.chars / max_rate - self.length return TimeValue("0.000")
[docs] def rate_slack(self, max_rate): """ The maximum time interval that can be stolen to this fragment while keeping it respecting the given max rate. For ``REGULAR`` fragments this value is the opposite of the ``rate_lack``. For ``NONSPEECH`` fragments this value is equal to the length of the fragment. For ``HEAD`` and ``TAIL`` fragments this value is ``0.000``, meaning that they cannot be stolen. :param max_rate: the maximum rate (characters/second) :type max_rate: :class:`~aeneas.exacttiming.Decimal` :rtype: :class:`~aeneas.exacttiming.TimeValue` .. versionadded:: 1.7.0 """ if self.fragment_type == self.REGULAR: return -self.rate_lack(max_rate) elif self.fragment_type == self.NONSPEECH: return self.length else: return TimeValue("0.000")