o
    %Ý«i€   ã                   @   s8   d Z dd„ Zdd„ Zdd„ Zdd„ Zd	d
„ Zdd„ ZdS )zžCalculates Emotion Diarization Error Rate (EDER) which is the sum of Missed Emotion (ME),
False Alarm (FA), and Confusion (CF).

Authors
 * Yingzhi Wang 2023
c                    sÐ   t |ƒ}g }tt| ƒƒD ]‰ |ˆ  }|| }| |||| ˆ  g¡ qt|ƒ}t|ƒdkr0t|ƒ}t|||ƒ}	d}
|	D ]'‰ ‡ fdd„|D ƒ}ˆ d ˆ d g}|D ]}t||d |d gƒ}|
|7 }
qOq:d|
|  S )aÌ  Calculates the EDER value

    Arguments
    ---------
    prediction: list
        a list of frame-wise predictions of the utterance
    id: str
        id of the utterance
    duration: float
        duration of the utterance
    emotion: list of dicts
        the ground truth emotion and its duration,
        e.g. [{'emo': 'angry', 'start': 1.016, 'end': 6.336}]
    window_length: float
        the frame length used for frame-wise prediction
    stride: float
        the frame length used for frame-wise prediction

    Returns
    -------
    float: the calculated EDER for the utterance

    Example
    -------
    >>> from speechbrain.utils.EDER import EDER
    >>> prediction=['n', 'n', 'n', 'a', 'a', 'a']
    >>> id="spk1_1"
    >>> duration=1.22
    >>> emotion=[{'emo': 'angry', 'start': 0.39, 'end': 1.10}]
    >>> window_length = 0.2
    >>> stride = 0.2
    >>> EDER(prediction, id, duration, emotion, window_length, stride)
    0.2704918032786885
    é   é    c                    s    g | ]}|d  ˆ d  kr|‘qS )é   © )Ú.0Úelement©Úir   úJ/home/ubuntu/.local/lib/python3.10/site-packages/speechbrain/utils/EDER.pyÚ
<listcomp>;   s     zEDER.<locals>.<listcomp>é   )ÚfloatÚrangeÚlenÚappendÚ!merge_ssegs_same_emotion_adjacentÚdistribute_overlapÚreference_to_lolÚ
getOverlap)Ú
predictionÚidÚdurationÚemotionÚwindow_lengthÚstrideÚlolÚstartÚendÚrefÚ
good_predsÚ
candidatesÚref_intervalÚ	candidateÚoverlapr   r   r	   ÚEDER	   s&   #
þr#   c                 C   s*   t dt| d |d ƒt | d |d ƒ ƒS )aV  Get the overlapped length of two intervals

    Arguments
    ---------
    a : list
    b : list

    Returns
    -------
    float: overlapped length

    Example
    -------
    >>> from speechbrain.utils.EDER import getOverlap
    >>> interval1=[1.2, 3.4]
    >>> interval2=[2.3, 4.5]
    >>> getOverlap(interval1, interval2)
    1.1
    r   r   )ÚmaxÚmin)ÚaÚbr   r   r	   r   D   s   *r   c                 C   s   || krdS dS )aÔ  Returns True if segments are overlapping.

    Arguments
    ---------
    end1 : float
        End time of the first segment.
    start2 : float
        Start time of the second segment.

    Returns
    -------
    overlapped : bool
        True of segments overlapped else False.

    Example
    -------
    >>> from speechbrain.processing import diarization as diar
    >>> diar.is_overlapped(5.5, 3.4)
    True
    >>> diar.is_overlapped(5.5, 6.4)
    False
    FTr   )Úend1Ústart2r   r   r	   Úis_overlapped[   s   r*   c                 C   s¢   g }| d }d}t dt| ƒƒD ]4}| | }t|d |d ƒr<|d |d kr<|d |d< |t| ƒd kr;d}| |¡ q| |¡ |}q|du rO| | d ¡ |S )aÛ  Merge adjacent sub-segs if they are the same emotion.

    Arguments
    ---------
    lol : list of list
        Each list contains [utt_id, sseg_start, sseg_end, emo_label].

    Returns
    -------
    new_lol : list of list
        new_lol contains adjacent segments merged from the same emotion ID.
    Example
    -------
    >>> from speechbrain.utils.EDER import merge_ssegs_same_emotion_adjacent
    >>> lol=[['u1', 0.0, 7.0, 'a'],
    ... ['u1', 7.0, 9.0, 'a'],
    ... ['u1', 9.0, 11.0, 'n'],
    ... ['u1', 11.0, 13.0, 'n'],
    ... ['u1', 13.0, 15.0, 'n'],
    ... ['u1', 15.0, 16.0, 'a']]
    >>> merge_ssegs_same_emotion_adjacent(lol)
    [['u1', 0.0, 9.0, 'a'], ['u1', 9.0, 15.0, 'n'], ['u1', 15.0, 16.0, 'a']]
    r   Fr   r   r   Téÿÿÿÿ©r   r   r*   r   )r   Únew_lolÚssegÚflagr   Ú	next_ssegr   r   r	   r   x   s    "
€
r   c                 C   sŽ   t |ƒdks
J dƒ‚g }|d d }|d d }|dkr%| | d|dg¡ | | |||d d d g¡ t|ƒ}||k rE| | ||dg¡ |S )aè  Change reference to a list of list

    Arguments
    ---------
    id: str
        id of the utterance
    duration: float
        duration of the utterance
    emotion: list of dicts
        the ground truth emotion and its duration,
        e.g. [{'emo': 'angry', 'start': 1.016, 'end': 6.336}]

    Returns
    -------
    lol : list of list
        It has each list structure as [rec_id, sseg_start, sseg_end, spkr_id].

    Example
    -------
    >>> from speechbrain.utils.EDER import reference_to_lol
    >>> id="u1"
    >>> duration=8.0
    >>> emotion=[{'emo': 'angry', 'start': 1.016, 'end': 6.336}]
    >>> reference_to_lol(id, duration, emotion)
    [['u1', 0, 1.016, 'n'], ['u1', 1.016, 6.336, 'a'], ['u1', 6.336, 8.0, 'n']]
    r   zXNotImplementedError: The solution is only implemented for one-emotion utterance for now.r   r   r   ÚnÚemo)r   r   r   )r   r   r   r   r   r   r   r   r	   r   ©   s   ÿr   c                 C   sæ   g }| d }t dt| ƒƒD ]^}| | }t|d |d ƒrR|d |d  }|d |d  |d< |d |d  |d< t|ƒdkrD| |¡ n|d |krO| |¡ |}qt|ƒdkr^| |¡ n|d |kri| |¡ |}q| |¡ |S )a	  Distributes the overlapped speech equally among the adjacent segments
    with different emotions.

    Arguments
    ---------
    lol : list of list
        It has each list structure as [rec_id, sseg_start, sseg_end, spkr_id].

    Returns
    -------
    new_lol : list of list
        It contains the overlapped part equally divided among the adjacent
        segments with different emotion IDs.

    Example
    -------
    >>> from speechbrain.processing import diarization as diar
    >>> lol = [['r1', 5.5, 9.0, 's1'],
    ... ['r1', 8.0, 11.0, 's2'],
    ... ['r1', 11.5, 13.0, 's2'],
    ... ['r1', 12.0, 15.0, 's1']]
    >>> diar.distribute_overlap(lol)
    [['r1', 5.5, 8.5, 's1'], ['r1', 8.5, 11.0, 's2'], ['r1', 11.5, 12.5, 's2'], ['r1', 12.5, 15.0, 's1']]
    r   r   r   g       @r+   r,   )r   r-   r.   r   r0   r"   r   r   r	   r   Õ   s(   


r   N)Ú__doc__r#   r   r*   r   r   r   r   r   r   r	   Ú<module>   s    ;1,