o
    Si8                     @   s   d dl Z d dlZd dlmZmZ d dlmZ d dlmZ d dl	m
Z
mZmZ d dlZd dlmZmZmZ eG dd dZeG d	d
 d
Z	ddededee defddZdedeeef fddZdejdededededeeef dejfddZdS )    N)asdict	dataclass)isclose)Path)ListOptionalUnion)PathlikeSecondsfastcopyc                   @   s   e Zd ZU dZeed< eed< eed< ee ed< edefddZ	ede
fd	d
Zede
fddZdefddZededd fddZdejfddZdedd fddZdde
dd fddZdd ZdS )Arraya  
    The Array manifest describes a numpy array that is stored somewhere: it might be
    in an HDF5 file, a compressed numpy file, on disk, in the cloud, etc.
    Array helps abstract away from the actual storage mechanism and location by
    providing a method called :meth:`.Array.load`.

    We don't assume anything specific about the array itself: it might be
    a feature matrix, an embedding, network activations, posteriors, alignment, etc.
    However, if the array has a temporal component, it is better to use the
    :class:`.TemporalArray` manifest instead.

    Array manifest can be easily created by calling
    :meth:`lhotse.features.io.FeaturesWriter.store_array`, for example::

        >>> from lhotse import NumpyHdf5Writer
        >>> ivector = np.random.rand(300)
        >>> with NumpyHdf5Writer('ivectors.h5') as writer:
        ...     manifest = writer.store_array('ivec-1', ivector)
    storage_typestorage_pathstorage_keyshapereturnc                 C   
   t | jS Nlenr   self r   @/home/ubuntu/.local/lib/python3.10/site-packages/lhotse/array.pyndim2      
z
Array.ndimc                 C   s   ddl m} || jS )Nr   )is_in_memory)lhotse.features.ior   r   )r   r   r   r   r   r   6   s   
zArray.is_in_memoryc                 C   s
   | j dkS )Nshar)r   r   r   r   r   is_placeholder<   r   zArray.is_placeholderc                 C      t | S r   r   r   r   r   r   to_dict@      zArray.to_dictdatac                 C   s.   d|v rd|v rd|vrd |d< | di |S )Nr   r   r   r   r   )clsr$   r   r   r   	from_dictC   s
   zArray.from_dictc                 C   s(   ddl m} || j| j}|| jS )z=
        Load the array from the underlying storage.
        r   
get_reader)r   r(   r   r   readr   )r   r(   storager   r   r   loadM   s   z
Array.loadpathc                 C   s   t | tt|| j dS )t
        Return a copy of the array with ``path`` added as a prefix
        to the ``storage_path`` member.
        )r   )r   strr   r   r   r,   r   r   r   with_path_prefixX   s   zArray.with_path_prefixFlilcomc                 C   sl   ddl m} | jdv r| S |  }t|jtjr!|r!|d }n|d }|d|}t	|j
|d| jdS )Nr   get_memory_writermemory_lilcommemory_writerr5   
memory_raw r   r   r   r   )r   r3   r   r+   np
issubdtypedtypefloatingwriter   namer   )r   r1   r3   arrwriterr$   r   r   r   move_to_memory_   s   

zArray.move_to_memoryc              	   C   s6   d| j  d| j dt| jtr| jnd d| j d	S )NzArray(storage_type='z', storage_path='z', storage_key='z<binary-data>z	', shape=))r   r   
isinstancer   r.   r   r   r   r   r   __repr__r   s   zArray.__repr__N)F)__name__
__module____qualname____doc__r.   __annotations__r   intpropertyr   boolr   r   dictr"   classmethodr&   r:   ndarrayr+   r	   r0   rB   rE   r   r   r   r   r      s&   
 	r   c                	   @   s6  e Zd ZU dZeed< eed< eed< eed< ede	fddZ
ede	fd	d
Zede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edefddZdefddZededd fddZ		d&dee dee dejfddZdedd fdd Z	!		"d'dedee d#e	dd fd$d%ZdS )(TemporalArraya  
    The :class:`.TemporalArray` manifest describes a numpy array that is stored somewhere:
    it might be in an HDF5 file, a compressed numpy file, on disk, in the cloud, etc.
    Like :class:`.Array`, it helps abstract away from the actual storage mechanism
    and location by providing a method called :meth:`.TemporalArray.load`.

    Unlike with :class:`.Array`, we assume that the array has a temporal dimension.
    It allows us to perform partial reads for sub-segments of the data if the underlying
    ``storage_type`` allows that.

    :class:`.TemporalArray` manifest can be easily created by calling
    :meth:`lhotse.features.io.FeaturesWriter.store_array` and specifying arguments
    related to its temporal nature; for example::

        >>> from lhotse import NumpyHdf5Writer
        >>> alignment = np.random.randint(500, size=131)
        >>> assert alignment.shape == (131,)
        >>> with NumpyHdf5Writer('alignments.h5') as writer:
        ...     manifest = writer.store_array(
        ...         key='ali-1',
        ...         value=alignment,
        ...         frame_shift=0.04,  # e.g., 10ms frames and subsampling_factor=4
        ...         temporal_dim=0,
        ...         start=0
        ...     )
    arraytemporal_dimframe_shiftstartr   c                 C      | j jS r   )rR   r   r   r   r   r   r         zTemporalArray.is_in_memoryc                 C   rV   r   )rR   r   r   r   r   r   r      rW   zTemporalArray.is_placeholderc                 C   rV   r   )rR   r   r   r   r   r   r      rW   zTemporalArray.shapec                 C   r   r   r   r   r   r   r   r      r   zTemporalArray.ndimc                 C   s   | j | j S r   )r   rS   r   r   r   r   
num_frames      zTemporalArray.num_framesc                 C   s   | j | j S r   )rX   rT   r   r   r   r   duration   rY   zTemporalArray.durationc                 C   s   | j | j S r   rU   rZ   r   r   r   r   end   rY   zTemporalArray.endc                 C   r    r   r!   r   r   r   r   r"      r#   zTemporalArray.to_dictr$   c                 C   s"   t |d}| dd|i|S )NrR   r   )r   r&   pop)r%   r$   rR   r   r   r   r&      s   zTemporalArray.from_dictNrZ   c                 C   s   ddl m} || jj| jj}d\}}|du r| j}|| jd k r2td| d| j d| j d	t|| jsGt	|| j | j
| j| j d
}|durY|t	|| j
| j| j d
 }|j| jj||dS )a  
        Load the array from the underlying storage.
        Optionally perform a partial read along the ``temporal_dim``.

        :param start: when specified, we'll offset the read by ``start`` after
            converting it to a number of frames based on ``self.frame_shift``.
        :param duration: when specified, we'll limit the read to a number of
            frames equivalent to ``duration`` under ``self.frame_shift``.
        :return: A numpy array or a relevant slice of it.
        r   r'   )r   NNgh㈵>z Cannot load array starting from zs. The available range is (z, z
) seconds.)rT   	max_index)left_offset_framesright_offset_frames)r   r(   rR   r   r   rU   
ValueErrorr\   r   seconds_to_framesrT   r   rS   r)   r   )r   rU   rZ   r(   r*   r_   r`   r   r   r   r+      s<   

zTemporalArray.loadr,   c                 C   s   t | | j|dS )r-   )rR   )r   rR   r0   r/   r   r   r   r0     s   zTemporalArray.with_path_prefixr   Fr1   c           	      C   s   ddl m} | jjdv r| S | j||d}t|jtjr%|r%|d }n|d }|	d|}t
t|j|dt|jd| j| jd	d
}|jdgkrOtd |S )Nr   r2   r4   r[   r5   r7   r8   r9   g        )rR   rS   rT   rU   zA TemporalArray with shape [0] encountered. If this is not expected and you're working with long-recording data, make sure you did set the 'start' attribute properly.)r   r3   rR   r   r+   r:   r;   r<   r=   r>   rQ   r   r?   listr   rS   rT   warningswarn)	r   rU   rZ   r1   r3   r@   rA   r$   outr   r   r   rB     s0   
zTemporalArray.move_to_memory)NN)r   NF)rF   rG   rH   rI   r   rJ   rK   r
   rL   rM   r   r   r   r   r   rX   rZ   r\   rN   r"   rO   r&   r   r:   rP   r+   r	   r0   rB   r   r   r   r   rQ   }   sZ   
 
3	rQ   rZ   rT   r^   r   c                 C   sH   | dksJ t tt| | ddjdtjd}|dur"t||S |S )z
    Convert time quantity in seconds to a frame index.
    It takes the shape of the array into account and limits
    the possible indices values to be compatible with the shape.
    r      )ndigits)roundingN)rK   decimalDecimalroundquantizeROUND_HALF_UPmin)rZ   rT   r^   indexr   r   r   rb   5  s   
rb   raw_datac                 C   s2   d| v r	t | S d| v rt| S td|  )z
    Figures out the right manifest type to use for deserialization.

    :param raw_data: The result of calling ``.to_dict`` on :class:`.Array`
        or :class:`.TemporalArray`.
    :return an :class:`.Array.` or :class:`.TemporalArray` instance.
    rR   r   zCannot deserialize array from: )rQ   r&   r   ra   )rq   r   r   r   deserialize_arrayK  s
   

rr   rR   rS   offsetpadded_duration	pad_valuec           
   
      s   | j  }t||d}|| }|dks%J d| j  d| d| d| d	|dkr+| S t||d |  dkr?d d	8  dksGJ d
 fddt| j D }	tj| |	d|dS )a  
    Pad a numpy array guided by duration based constraints.

    Example::

        >>> arr = np.array([1, 2, 3])
        >>> pad_array(arr, temporal_dim=0, frame_shift=0.1,
        ...           offset=0.1, padded_duration=0.6, pad_value=0)
        array([0, 1, 2, 3, 0, 0])

    :param array: array to be padded.
    :param temporal_dim: time dimension index.
    :param frame_shift: time interval (seconds) between the starts of consecutive frames.
    :param offset: how much padding goes before the array (seconds).
    :param padded_duration: expected duration of array after padding (seconds).
    :param pad_value: value used for padding.
    :return: a padded array.
    )rT   r   z8Invalid argument values for pad_array: array with shape z( cannot be padded to padded_duration of z* as it results in smaller temporal_dim of z frames (under frame_shift=z).   zSomething went wrong...c                    s$   g | ]\}}|kr fnd qS ))r   r   r   ).0dimsizeleft_pad_framesright_pad_framesrS   r   r   
<listcomp>  s    zpad_array.<locals>.<listcomp>constant)	pad_widthmodeconstant_values)r   rb   	enumerater:   pad)
rR   rS   rT   rs   rt   ru   array_framestotal_framestotal_padding_framesr   r   r{   r   	pad_arrayZ  s4   


r   r   )rj   rd   dataclassesr   r   mathr   pathlibr   typingr   r   r   numpyr:   lhotse.utilsr	   r
   r   r   rQ   rK   rb   rN   rr   rP   floatr   r   r   r   r   <module>   sN    o 9

