
    i-(                        S r SSKrSSKrSSKrSSKrSSKrSSKrSSK	J
r
  SSKJrJrJr  \R                  " \5      rS\4S jrS\S\S\R*                  4S	 jrS
\S\S\R*                  4S jrS\R*                  S\S\
4S jrS\
S\R*                  4S jr   S!S\R*                  S\S\S\S\S\R*                  4S jjr   S"S\
S\S\S\S\
4
S jjr   S#S\R*                  S\S\S\S\R*                  4
S jjr   S$S\R*                  S\S\S\S\S\R*                  4S jjr S%S\ \R*                     S\S\S\R*                  4S  jjr!g)&a  Audio I/O and processing utilities.

Provides functions for loading, resampling, silence removal,
chunking, cross-fading, and format conversion.

All public functions in this module operate on **numpy float32 arrays**
with shape ``(C, T)`` (channels-first).
    N)AudioSegment)detect_leading_silencedetect_nonsilentsplit_on_silence
audio_pathc                      [         R                  " U SSS9u  pUR                  U4$ ! [         aD    SSKnUR                  U SSS9u  pUR                  S:X  a  U[        R                  SS24   nX4s $ f = f)	u-  Load audio from a file path, returning (data, sample_rate).

Tries two backends in order:
1. soundfile — covers WAV/FLAC/OGG etc., no ffmpeg needed.
2. librosa — covers MP3/M4A etc. via audioread + ffmpeg.

Returns:
    (data, sample_rate) where data is a numpy float32 array of
    shape (C, T).
float32Tdtype	always_2dr   NFsrmono   )	sfreadT	Exceptionlibrosaloadndimnpnewaxis)r   datar   r   s       1/mnt/workspace/OmniVoice/omnivoice/utils/audio.pyload_waveformr   -   su    
77:Y$Gvvrz <<
t%<@99>

A&Dxs   %( AA65A6sampling_ratereturnc                     [        U 5      u  p#UR                  S   S:  a  [        R                  " USSS9nX1:w  a@  [        R
                  R                  [        R                  " U5      X1S9R                  5       nU$ )zLoad a waveform from file and resample to the target rate.

Parameters:
    audio_path: path of the audio.
    sampling_rate: target sampling rate.

Returns:
    Numpy float32 array of shape (1, T).
r   r   Taxiskeepdims	orig_freqnew_freq)
r   shaper   mean
torchaudio
functionalresampletorch
from_numpynumpy)r   r   r   r   s       r   
load_audior.   E   st     Z(HDzz!}qwwt!d3	$$--T"b . 

%' 	 K    rawc                    [         R                  " U 5      n [        R                  " USSS9u  p4UR                  nUR                  S   S:  a  [        R                  " USSS	9nXA:w  a@  [        R                  R!                  ["        R$                  " U5      XAS
9R'                  5       nU$ ! [
         aR    SSKnUR                  S5        UR                  USSS9u  p4UR                  S:X  a  U[        R                  SS24   n Nf = f)zLoad audio from in-memory bytes and resample.

Parameters:
    raw: raw audio file bytes (e.g. from WebDataset).
    sampling_rate: target sampling rate.

Returns:
    Numpy float32 array of shape (1, T).
r	   Tr
   r   NFr   r   r    r#   )ioBytesIOr   r   r   r   r   seekr   r   r   r   r&   r'   r(   r)   r*   r+   r,   r-   )r0   r   bufr   r   r   s         r   load_audio_bytesr6   [   s     **S/C	'773i4@vv zz!}qwwt!d3	$$--T"b . 

%' 	 K  '<<5<999>

A&D's   $B, ,ADDaudiosample_ratec                    U S-  R                  SS5      R                  [        R                  5      nUR                  S   S:  a  UR
                  R                  5       n[        UR                  5       SUU R                  S   S9$ )zFConvert a numpy float32 array of shape (C, T) to a pydub AudioSegment.      @i i  r   r      )r   sample_width
frame_ratechannels)	clipastyper   int16r&   r   flattenr   tobytes)r7   r8   	audio_ints      r   numpy_to_audiosegmentrE      st    &&vu5<<RXXFIqAKK'')	 Q	 r/   asegc                 $   [         R                  " U R                  5       5      R                  [         R                  5      S-  nU R
                  S:X  a  U[         R                  SS24   $ UR                  SU R
                  5      R                  $ )zFConvert a pydub AudioSegment to a numpy float32 array of shape (C, T).r:   r   N)	r   arrayget_array_of_samplesr@   r	   r>   r   reshaper   )rF   r   s     r   audiosegment_to_numpyrL      sg    88D--/077

CgMD}}BJJM""<<DMM*,,,r/   mid_sillead_sil	trail_silc                     [        X5      nUS:  a.  [        UUSUSS9n[        R                  " SS9nU H  nXW-  nM	     [	        XSUS5      n[        U5      $ )ak  Remove middle silences longer than *mid_sil* ms and trim edge silences.

Parameters:
    audio: numpy array with shape (C, T).
    sampling_rate: sampling rate of the audio.
    mid_sil: middle-silence threshold in ms (0 to skip).
    lead_sil: kept leading silence in ms.
    trail_sil: kept trailing silence in ms.

Returns:
    Numpy array with shape (C, T').
r   
   )min_silence_lensilence_threshkeep_silence	seek_step)duration)rE   r   r   silentremove_silence_edgesrL   )r7   r   rM   rN   rO   wavenon_silent_segssegs           r   remove_silencer]      sl    & !6D{*# 
 ""A."CKD #  	3?D &&r/   silence_thresholdc                     [        XS9n[        SXA-
  5      nXS n U R                  5       n [        XS9n[        SXB-
  5      nXS n U R                  5       n U $ )z:Remove edge silences, keeping *lead_sil* / *trail_sil* ms.)r^   r   N)r   maxreverse)r7   rN   rO   r^   	start_idxs        r   rY   rY      se     'uRIAy+,I*EMMOE&uRIAy,-I*EMMOELr/   pad_durationfade_durationc                    U R                   S   S:X  a  U $ [        X#-  5      n[        X-  5      nU R                  5       nUS:  a  [        XFR                   S   S-  5      nUS:  a  [        R
                  " SSU[        R                  S9[        R                  SS24   nUSSU24==   U-  ss'   [        R
                  " SSU[        R                  S9[        R                  SS24   n	USU* S24==   U	-  ss'   US:  aE  [        R                  " UR                   S   U4UR                  S9n
[        R                  " XU
/SS9nU$ )	a9  Apply fade-in/out and pad with silence to prevent clicks.

Args:
    audio: numpy array of shape (C, T).
    pad_duration: silence padding duration per side (seconds).
    fade_duration: fade curve duration (seconds).
    sample_rate: audio sampling rate.

Returns:
    Processed numpy array of shape (C, T_new).
rH   r   r;   r   r   N.r!   )r&   intcopyminr   linspacer	   r   zerosr   concatenate)r7   rc   rd   r8   fade_samplespad_samples	processedkfade_infade_outsilences              r   fade_and_pad_audioru      s)   " {{2!}23Ll01K

Iaoob1Q67q5kk!Q<RZZ]KGc2A2g'){{1a"**=bjj!mLHcA23h8+Q((__Q-//
 NNG#@rJ	r/   max_durationmin_durationtrim_thresholdc                 @   U R                   S   U-  nXT::  a  U $ [        X5      n[        USSSS9nU(       d  U $ [        US-  5      n[        US-  5      n	Sn
U H  u  pX:  a  X::  a  Un
X:  d  M    O   X:  a  [	        U[        U5      5      n
USU
 n[        U5      $ )	a  Trim audio to <= *max_duration* by splitting at the largest silence gap.

Only trims when the audio exceeds *trim_threshold* seconds.

Args:
    audio: numpy array of shape (C, T).
    sampling_rate: audio sampling rate.
    max_duration: maximum duration in seconds.
    min_duration: minimum duration in seconds.
    trim_threshold: only trim if audio is longer than this (seconds).

Returns:
    Trimmed numpy array.
rH   d   irR   )rS   rT   rV   i  r   N)r&   rE   r   rh   rj   lenrL   )r7   r   rv   rw   rx   rW   r\   	nonsilentmax_msmin_ms
best_splitstartendtrimmeds                 r   trim_long_audior      s    * {{2.H!

5C SI $%F$%FJ
%/J<	   S*
+:G ))r/   chunkssilence_durationc                 >   [        U 5      S:X  a  U S   $ [        X!-  5      nUS-  nUnU S   R                  5       nU SS  GHW  nU/n[        XFR                  S   5      n	U	S:  aO  [
        R                  " SSU	[
        R                  S9[
        R                  SS24   n
US   SU	* S24==   U
-  ss'   UR                  [
        R                  " U S   R                  S   U4[
        R                  S95        UR                  5       n[        XKR                  S   5      nUS:  aK  [
        R                  " SSU[
        R                  S9[
        R                  SS24   nUSSU24==   U-  ss'   UR                  U5        [
        R                  " USS9nGMZ     U$ )	a  Concatenate audio chunks with silence gaps and cross-fade at boundaries.

Args:
    chunks: list of numpy arrays, each (C, T).
    sample_rate: audio sample rate.
    silence_duration: total silence gap duration in seconds.

Returns:
    Merged numpy array (C, T_total).
r   r      NrH   rf   .rg   )r{   rh   ri   rj   r&   r   rk   r	   r   appendrl   rm   )r   r8   r   total_nfade_n	silence_nmergedchunkpartsfout_nw_outrr   fin_nw_ins                 r   cross_fade_chunksr   -  sj    6{aay"01G\FIAY^^FV\\"-.A:KK1fBJJ?

ANE"IcF78m$-$RXXvayq19=RZZPQ**,FMM"-.19;;q!U"**=bjj!mLDC%K D( WB/# & Mr/   ),  rz   r   )rz   r   rQ   )皙?r   i]  )g      .@g      @g      4@)g333333?)"__doc__r2   loggingr-   r   	soundfiler   r+   r(   pydubr   pydub.silencer   r   r   	getLogger__name__loggerstrr   rh   ndarrayr.   bytesr6   rE   rL   r]   floatrY   ru   r   listr    r/   r   <module>r      s  $ 
       T T			8	$c 03 s rzz ,%  

 L
 
# 
, 
- - - #'::#'#' #' 	#'
 #' ZZ#'P "	  	
 , 	)::)) ) 	)
 ZZ)^  .*::.*.* .* 	.*
 .* ZZ.*h "*** * ZZ	*r/   