o
    piMl                     @   sx   d Z ddlZddlmZmZmZmZmZmZ ddl	m
Z
 ddlZddlmZ edddG d	d
 d
ZG dd dZdS )as  
#######
Segment
#######

.. plot:: pyplots/segment.py

:class:`pyannote.core.Segment` instances describe temporal fragments (*e.g.* of an audio file). The segment depicted above can be defined like that:

.. code-block:: ipython

  In [1]: from pyannote.core import Segment

  In [2]: segment = Segment(start=5, end=15)

  In [3]: print(segment)

It is nothing more than 2-tuples augmented with several useful methods and properties:

.. code-block:: ipython

  In [4]: start, end = segment

  In [5]: start

  In [6]: segment.end

  In [7]: segment.duration  # duration (read-only)

  In [8]: segment.middle  # middle (read-only)

  In [9]: segment & Segment(3, 12)  # intersection

  In [10]: segment | Segment(3, 12)  # union

  In [11]: segment.overlaps(3)  # does segment overlap time t=3?


Use `Segment.set_precision(ndigits)` to automatically round start and end timestamps to `ndigits` precision after the decimal point.
To ensure consistency between `Segment` instances, it is recommended to call this method only once, right after importing `pyannote.core.Segment`.

.. code-block:: ipython

  In [12]: Segment(1/1000, 330/1000) == Segment(1/1000, 90/1000+240/1000)
  Out[12]: False

  In [13]: Segment.set_precision(ndigits=4)

  In [14]: Segment(1/1000, 330/1000) == Segment(1/1000, 90/1000+240/1000)
  Out[14]: True

See :class:`pyannote.core.Segment` for the complete reference.
    N)UnionOptionalTupleListIteratorIterable   )	Alignment)	dataclassT)frozenorderc                   @   s  e Zd ZU dZdZeed< dZeed< ed-de	e
 fddZd	d
 Zdd ZedefddZedefddZdee fddZd.ddZd/ddZdd Zdd defddZdedefddZd0d d!Zd0d"d#Zd$edefd%d&Zd'd( Zd)d* Zd+d, ZdS )1Segmenta  
    Time interval

    Parameters
    ----------
    start : float
        interval start time, in seconds.
    end : float
        interval end time, in seconds.


    Segments can be compared and sorted using the standard operators:

    >>> Segment(0, 1) == Segment(0, 1.)
    True
    >>> Segment(0, 1) != Segment(3, 4)
    True
    >>> Segment(0, 1) < Segment(2, 3)
    True
    >>> Segment(0, 1) < Segment(0, 2)
    True
    >>> Segment(1, 2) < Segment(0, 3)
    False

    Note
    ----
    A segment is smaller than another segment if one of these two conditions is verified:

      - `segment.start < other_segment.start`
      - `segment.start == other_segment.start` and `segment.end < other_segment.end`

            startendNndigitsc                 C   s&   | du r
da dadS da d|   adS )a  Automatically round start and end timestamps to `ndigits` precision after the decimal point

        To ensure consistency between `Segment` instances, it is recommended to call this method only 
        once, right after importing `pyannote.core.Segment`.

        Usage
        -----
        >>> from pyannote.core import Segment
        >>> Segment.set_precision(2)
        >>> Segment(1/3, 2/3)
        <Segment(0.33, 0.67)>
        NFgư>T
   )AUTO_ROUND_TIMESEGMENT_PRECISION)r    r   I/home/ubuntu/.local/lib/python3.10/site-packages/pyannote/core/segment.pyset_precision   s
   zSegment.set_precisionc                 C   s   t | j| j tkS )u&  Emptiness

        >>> if segment:
        ...    # segment is not empty.
        ... else:
        ...    # segment is empty.

        Note
        ----
        A segment is considered empty if its end time is smaller than its
        start time, or its duration is smaller than 1μs.
        )boolr   r   r   selfr   r   r   __bool__      zSegment.__bool__c                 C   sL   t r$t| dt| jt d t  t| dt| jt d t  dS dS )zERound start and end up to SEGMENT_PRECISION precision (when required)r         ?r   N)r   object__setattr__intr   r   r   r   r   r   r   __post_init__   s    $zSegment.__post_init__returnc                 C   s   | r| j | j S dS )zSegment duration (read-only)r   )r   r   r   r   r   r   duration   s   zSegment.durationc                 C   s   d| j | j  S )zSegment mid-time (read-only)r   r   r   r   r   r   r   middle   s   zSegment.middlec                 c   s    | j V  | jV  dS )zmUnpack segment boundaries
        >>> segment = Segment(start, end)
        >>> start, end = segment
        Nr$   r   r   r   r   __iter__   s   zSegment.__iter__c                 C   s   t | j| jdS )z{Get a copy of the segment

        Returns
        -------
        copy : Segment
            Copy of the segment.
        r$   )r   r   r   r   r   r   r   copy   s   zSegment.copyotherc                 C   s   | j |j ko| j|jkS )zInclusion

        >>> segment = Segment(start=0, end=10)
        >>> Segment(start=3, end=10) in segment:
        True
        >>> Segment(start=5, end=15) in segment:
        False
        r$   r   r(   r   r   r   __contains__   s   	zSegment.__contains__c                 C   s(   t | j|j}t| j|j}t||dS )a  Intersection

        >>> segment = Segment(0, 10)
        >>> other_segment = Segment(5, 15)
        >>> segment & other_segment
        <Segment(5, 10)>

        Note
        ----
        When the intersection is empty, an empty segment is returned:

        >>> segment = Segment(0, 10)
        >>> other_segment = Segment(15, 20)
        >>> intersection = segment & other_segment
        >>> if not intersection:
        ...    # intersection is empty.
        r$   )maxr   minr   r   r   r(   r   r   r   r   r   __and__   s   zSegment.__and__c                 C   sD   | j |j k r|j | jt k p!| j |j ko| j |jt k p!| j |j kS )a  Check whether two segments intersect each other

        Parameters
        ----------
        other : Segment
            Other segment

        Returns
        -------
        intersect : bool
            True if segments intersect, False otherwise
        )r   r   r   r)   r   r   r   
intersects   s   
zSegment.intersectstc                 C   s   | j |ko	| j|kS )zCheck if segment overlaps a given time

        Parameters
        ----------
        t : float
            Time, in seconds.

        Returns
        -------
        overlap: bool
            True if segment overlaps time t, False otherwise.
        r$   r   r0   r   r   r   overlaps  r   zSegment.overlapsc                 C   s8   | s|S |s| S t | j|j}t| j|j}t||dS )a  Union

        >>> segment = Segment(0, 10)
        >>> other_segment = Segment(5, 15)
        >>> segment | other_segment
        <Segment(0, 15)>

        Note
        ----
        When a gap exists between the segment, their union covers the gap as well:

        >>> segment = Segment(0, 10)
        >>> other_segment = Segment(15, 20)
        >>> segment | other_segment
        <Segment(0, 20)
        r$   )r,   r   r+   r   r   r-   r   r   r   __or__  s   zSegment.__or__c                 C   s8   | r|st dt| j|j}t| j|j}t||dS )a  Gap

        >>> segment = Segment(0, 10)
        >>> other_segment = Segment(15, 20)
        >>> segment ^ other_segment
        <Segment(10, 15)

        Note
        ----
        The gap between a segment and an empty segment is not defined.

        >>> segment = Segment(0, 10)
        >>> empty_segment = Segment(11, 11)
        >>> segment ^ empty_segment
        ValueError: The gap between a segment and an empty segment is not defined.
        z>The gap between a segment and an empty segment is not defined.r$   )
ValueErrorr,   r   r+   r   r   r-   r   r   r   __xor__3  s   zSegment.__xor__secondsc           	      C   sv   ddl m} |dk }t|}||d}|jd|j  }|j}t|d\}}t|d\}}d|r1dnd	||||d
 f S )Nr   )	timedelta)r6   iQ i  <   z%s%02d:%02d:%02d.%03d- i  )datetimer7   absr6   daysmicrosecondsdivmod)	r   r6   r7   negativetdr>   hours	remainderminutesr   r   r   _str_helperO  s   
zSegment._str_helperc                 C   s$   | rd|  | j|  | jf S dS )zHuman-readable representation

        >>> print(Segment(1337, 1337 + 0.42))
        [ 00:22:17.000 -->  00:22:17.420]

        Note
        ----
        Empty segments are printed as "[]"
        z[%s --> %s]z[])rE   r   r   r   r   r   r   __str__\  s
   

zSegment.__str__c                 C   s   d| j | jf S )zrComputer-readable representation

        >>> Segment(1337, 1337 + 0.42)
        <Segment(1337, 1337.42)>
        z<Segment(%g, %g)>r$   r   r   r   r   __repr__k  s   zSegment.__repr__c                 C   sl   ddl m}m} |st|j| jjd dS ddl m} z|| W S  t	y5   td|  d Y dS w )zjIPython notebook support

        See also
        --------
        :mod:`pyannote.core.notebook`
        r   )MATPLOTLIB_IS_AVAILABLEMATPLOTLIB_WARNING)klassN)repr_segmentzBCouldn't import matplotlib to render the vizualization for object zY. To enable, install the required dependencies with 'pip install pyannore.core[notebook]')
notebookrH   rI   warningswarnformat	__class____name__rK   ImportError)r   rH   rI   rK   r   r   r   
_repr_png_s  s   

zSegment._repr_png_N)r"   r   )r(   r   )r(   r   r"   r   ) rQ   
__module____qualname____doc__r   float__annotations__r   staticmethodr   r    r   r   r!   propertyr#   r%   r   r&   r'   r*   r.   r   r/   r2   r3   r5   strrE   rF   rG   rS   r   r   r   r   r   ]   s0   
  



r   c                   @   s  e Zd ZdZdHddZedefd	d
ZedefddZedefddZ	edefddZ
dedefddZdIdededefddZ			dJdeedf dedee dee deejeee  f f
dd Zd!edeeef fd"d#Zd!edeeef fd$d%Zd&ed'edefd(d)Zd&ed'edefd*d+Zd,edefd-d.Zd/edefd0d1Zd2edefd3d4Zd2edefd5d6Zd7edefd8d9Z defd:d;Z!defd<d=Z"dKd>d?Z#defd@dAZ$dKdBdCZ%	dLdDeedf dEede&e fdFdGZ'dS )MSlidingWindowa  Sliding window

    Parameters
    ----------
    duration : float > 0, optional
        Window duration, in seconds. Default is 30 ms.
    step : float > 0, optional
        Step between two consecutive position, in seconds. Default is 10 ms.
    start : float, optional
        First start position of window, in seconds. Default is 0.
    end : float > `start`, optional
        Default is infinity (ie. window keeps sliding forever)

    Examples
    --------

    >>> sw = SlidingWindow(duration, step, start)
    >>> frame_range = (a, b)
    >>> frame_range == sw.toFrameRange(sw.toSegment(*frame_range))
    ... True

    >>> segment = Segment(A, B)
    >>> new_segment = sw.toSegment(*sw.toFrameRange(segment))
    >>> abs(segment) - abs(segment & new_segment) < .5 * sw.step

    >>> sw = SlidingWindow(end=0.1)
    >>> print(next(sw))
    [ 00:00:00.000 -->  00:00:00.030]
    >>> print(next(sw))
    [ 00:00:00.010 -->  00:00:00.040]
    Q?{Gz?r   Nc                 C   sd   |dkrt d|| _|dkrt d|| _|| _|d u r"tj| _n||kr*t d|| _d| _d S )Nr   z'duration' must be a float > 0.z'step' must be a float > 0.z#'end' must be greater than 'start'.)r4   _SlidingWindow__duration_SlidingWindow__step_SlidingWindow__startnpinf_SlidingWindow__end_SlidingWindow__i)r   r#   stepr   r   r   r   r   __init__  s   

zSlidingWindow.__init__r"   c                 C      | j S )z%Sliding window start time in seconds.)rc   r   r   r   r   r        zSlidingWindow.startc                 C   rj   )z#Sliding window end time in seconds.)rf   r   r   r   r   r     rk   zSlidingWindow.endc                 C   rj   )zSliding window step in seconds.)rb   r   r   r   r   rh     rk   zSlidingWindow.stepc                 C   rj   )z#Sliding window duration in seconds.)ra   r   r   r   r   r#     rk   zSlidingWindow.durationr0   c                 C   s$   t t|| j d| j  | j S )zClosest frame to timestamp.

        Parameters
        ----------
        t : float
            Timestamp, in seconds.

        Returns
        -------
        index : int
            Index of frame whose middle is the closest to `timestamp`

        r   )r    rd   rintrc   ra   rb   r1   r   r   r   closest_frame  s   zSlidingWindow.closest_framestrictfrom_durationmodec                 C   sh   |dkrt t|| j | j d S |dkr$t t|| j | j S |dkr2t t|| j S dS )a  Number of frames

        Parameters
        ----------
        from_duration : float
            Duration in seconds.
        mode : {'strict', 'loose', 'center'}
            In 'strict' mode, computes the maximum number of consecutive frames
            that can be fitted into a segment with duration `from_duration`.
            In 'loose' mode, computes the maximum number of consecutive frames
            intersecting a segment with duration `from_duration`.
            In 'center' mode, computes the average number of consecutive frames
            where the first one is centered on the start time and the last one
            is centered on the end time of a segment with duration
            `from_duration`.

        rn   r   loosecenterN)r    rd   floorr#   rh   rl   )r   ro   rp   r   r   r   samples  s   zSlidingWindow.samplesrq   FfocusTimelinefixedreturn_rangesc                    sf  ddl m} t|t|fsd}t|t||rp dur"d}t||r[g }t| D ],\}}	j|	 dd}
|dksI|
d d |d	 d krN||
7 }q,|
d d |d	 d< q,|S t	
 fd
d| D }t	|S dkr|jj j j }tt	|} du r|jj j }tt	|}||d f}
nj dd}||| f}
nqdkr|jj j }tt	|} du r|jj j j }tt	|}||d f}
n?j dd}||| f}
n1dkr|j} du r|j}||d f}
nj dd}||| f}
nd}t||r)t|
gS t	jt|
 t	jdS )a  Crop sliding window

        Parameters
        ----------
        focus : `Segment` or `Timeline`
        mode : {'strict', 'loose', 'center'}, optional
            In 'strict' mode, only indices of segments fully included in
            'focus' support are returned. In 'loose' mode, indices of any
            intersecting segments are returned. In 'center' mode, first and
            last positions are chosen to be the positions whose centers are the
            closest to 'focus' start and end times. Defaults to 'loose'.
        fixed : float, optional
            Overrides `Segment` 'focus' duration and ensures that the number of
            returned frames is fixed (which might otherwise not be the case
            because of rounding erros).
        return_ranges : bool, optional
            Return as list of ranges. Defaults to indices numpy array.

        Returns
        -------
        indices : np.array (or list of ranges)
            Array of unique indices of matching segments
        r   rv   z3"focus" must be a `Segment` or `Timeline` instance.Nz1'fixed' is not supported with `Timeline` 'focus'.Trp   rw   rx   r   r`   c                    s   g | ]}j | d dqS )Frz   )crop).0srw   rp   r   r   r   
<listcomp>@  s    z&SlidingWindow.crop.<locals>.<listcomp>rq   )rp   rn   rr   z4'mode' must be one of {'loose', 'strict', 'center'}.)dtype)timelinerv   
isinstancer   	TypeErrorr4   	enumeratesupportr{   rd   hstackuniquer   r#   rh   r    ceilr   rs   rt   rm   listarrayrangeint64)r   ru   rp   rw   rx   rv   msgrangesir}   rngindicesi_j_jnr   r~   r   r{     sh   

 




zSlidingWindow.cropsegmentc                 C      t dt | |S )Nz)Deprecated in favor of `segment_to_range`)rM   rN   DeprecationWarningsegment_to_range)r   r   r   r   r   segmentToRange     
zSlidingWindow.segmentToRangec                 C   s(   |  |j}t|j| j d }||fS )a  Convert segment to 0-indexed frame range

        Parameters
        ----------
        segment : Segment

        Returns
        -------
        i0 : int
            Index of first frame
        n : int
            Number of frames

        Examples
        --------

            >>> window = SlidingWindow()
            >>> print window.segment_to_range(Segment(10, 15))
            i0, n

        r   )rm   r   r    r#   rh   )r   r   i0r   r   r   r   r     s   zSlidingWindow.segment_to_ranger   r   c                 C   s   t dt | ||S )Nz1This is deprecated in favor of `range_to_segment`)rM   rN   r   range_to_segment)r   r   r   r   r   r   rangeToSegment  s   zSlidingWindow.rangeToSegmentc                 C   sH   | j |d | j  d| j  }|| j }|| }|dkr| j}t||S )ak  Convert 0-indexed frame range to segment

        Each frame represents a unique segment of duration 'step', centered on
        the middle of the frame.

        The very first frame (i0 = 0) is the exception. It is extended to the
        sliding window start time.

        Parameters
        ----------
        i0 : int
            Index of first frame
        n : int
            Number of frames

        Returns
        -------
        segment : Segment

        Examples
        --------

            >>> window = SlidingWindow()
            >>> print window.range_to_segment(3, 2)
            [ --> ]

        r   r   )rc   rb   ra   r   r   )r   r   r   r   r#   r   r   r   r   r     s   #

zSlidingWindow.range_to_segmentnSamplesc                 C   r   )Nz4This is deprecated in favor of `samples_to_duration`)rM   rN   r   samples_to_duration)r   r   r   r   r   samplesToDuration  r   zSlidingWindow.samplesToDuration	n_samplesc                 C   s   |  d|jS )zReturns duration of samplesr   )r   r#   )r   r   r   r   r   r     s   z!SlidingWindow.samples_to_durationr#   c                 C   r   )Nz4This is deprecated in favor of `duration_to_samples`)rM   rN   r   duration_to_samplesr   r#   r   r   r   durationToSamples  r   zSlidingWindow.durationToSamplesc                 C   s   |  td|d S )zReturns samples in durationr   r   )r   r   r   r   r   r   r     s   z!SlidingWindow.duration_to_samplesr   c                 C   s0   | j || j  }|| jkrdS t||| j dS )z
        Parameters
        ----------
        i : int
            Index of sliding window position

        Returns
        -------
        segment : :class:`Segment`
            Sliding window at ith position

        Nr$   )rc   rb   rf   r   ra   )r   r   r   r   r   r   __getitem__  s   
zSlidingWindow.__getitem__c                 C   s   |   S rT   )__next__r   r   r   r   next  s   zSlidingWindow.nextc                 C   s&   |  j d7  _ | | j  }|r|S t )Nr   )rg   StopIteration)r   windowr   r   r   r     s
   
zSlidingWindow.__next__c                 C   s
   d| _ | S )a  Sliding window iterator

        Use expression 'for segment in sliding_window'

        Examples
        --------

        >>> window = SlidingWindow(end=0.1)
        >>> for segment in window:
        ...     print(segment)
        [ 00:00:00.000 -->  00:00:00.030]
        [ 00:00:00.010 -->  00:00:00.040]
        [ 00:00:00.020 -->  00:00:00.050]
        [ 00:00:00.030 -->  00:00:00.060]
        [ 00:00:00.040 -->  00:00:00.070]
        [ 00:00:00.050 -->  00:00:00.080]
        [ 00:00:00.060 -->  00:00:00.090]
        [ 00:00:00.070 -->  00:00:00.100]
        [ 00:00:00.080 -->  00:00:00.110]
        [ 00:00:00.090 -->  00:00:00.120]
        r`   )rg   r   r   r   r   r&     s   zSlidingWindow.__iter__c                 C   s@   t | jr
td| | j}| | r|d7 }| | s|}|S )zNumber of positions

        Equivalent to len([segment for segment in window])

        Returns
        -------
        length : int
            Number of positions taken by the sliding window
            (from start times to end times)

        zinfinite sliding window.r   )rd   isinfrf   r4   rm   )r   r   lengthr   r   r   __len__*  s   zSlidingWindow.__len__c                 C   s.   | j }| j}| j}| j}| j||||d}|S )zDuplicate sliding windowr#   rh   r   r   )r#   rh   r   r   rP   )r   r#   rh   r   r   sliding_windowr   r   r   r'   C  s   zSlidingWindow.copyr   
align_lastc           
      c   s    ddl m} t||r|}nt|tr||gd}ndt| d}t||D ]7}|j| jk r2q)t| j| j|j	|j
d}|D ]}||v rK|V  |}	q@|r`|	j
|j
k r`t|j
| j |j
dV  q)dS )	a  Slide window over support

        Parameter
        ---------
        support : Segment or Timeline
            Support on which to slide the window.
        align_last : bool, optional
            Yield a final segment so that it aligns exactly with end of support.

        Yields
        ------
        chunk : Segment

        Example
        -------
        >>> window = SlidingWindow(duration=2., step=1.)
        >>> for chunk in window(Segment(3, 7.5)):
        ...     print(tuple(chunk))
        (3.0, 5.0)
        (4.0, 6.0)
        (5.0, 7.0)
        >>> for chunk in window(Segment(3, 7.5), align_last=True):
        ...     print(tuple(chunk))
        (3.0, 5.0)
        (4.0, 6.0)
        (5.0, 7.0)
        (5.5, 7.5)
        r   ry   )segmentsz>"support" must be either a Segment or a Timeline instance (is )r   r$   N)pyannote.corerv   r   r   typer   r#   r]   rh   r   r   )
r   r   r   rv   r   r   r   r   r}   lastr   r   r   __call__N  s<    

zSlidingWindow.__call__)r^   r_   r   N)rn   )rq   NF)r"   r]   )F)(rQ   rU   rV   rW   ri   r[   rX   r   r   rh   r#   r    rm   r	   rt   r   r   r   r   rd   ndarrayr   r{   r   r   r   r   r   r   r   r   r   r   r   r   r&   r   r'   r   r   r   r   r   r   r]     s^    
 
-
	

r]   )rW   rM   typingr   r   r   r   r   r   utils.typesr	   numpyrd   dataclassesr
   r   r]   r   r   r   r   <module>   s   6 
  ,