o
    ‡ØFiÚ0  ã                   @   s
  d Z ddlZddlm  mZ ddlZddlZej	 
dej	 ej	 e¡dd¡¡ ddlZddlZddlmZ ddlmZ G dd„ dƒZG d	d
„ d
ƒZG dd„ dƒZG dd„ dƒZG dd„ dƒZG dd„ dƒZG dd„ dƒZdd„ Zedkrƒeƒ Ze er~dnd¡ dS dS )z©
test_dsp_numpy.py - Unit tests for DSP functions

Verifies that audio arrays maintain correct shapes after processing
and that DSP operations produce expected results.
é    Nz..Úsrc)ÚAudioEngine)ÚGENRE_PRESETSc                   @   ó(   e Zd ZdZdd„ Zdd„ Zdd„ ZdS )	ÚTestAudioEngineBasicsz%Test basic AudioEngine functionality.c                 C   sŒ   t dd| _d| j_d}t d|t| jj| ƒ¡}t dtj d | ¡ tj	¡}t dtj d | ¡ tj	¡}t 
||f¡| _|| _d	S )
úSet up test fixtures.Útemp©Útemp_diréD¬  ç      ð?r   é   é¸  ip  N)r   ÚengineÚsample_rateÚnpÚlinspaceÚintÚsinÚpiÚastypeÚfloat32Úcolumn_stackÚstereo_audioÚ
mono_audio)ÚselfÚdurationÚtÚleftÚright© r    ú8/home/user/Projects/AutoRemaster/tests/test_dsp_numpy.pyÚsetup_method   s     
z"TestAudioEngineBasics.setup_methodc                 C   s.  | j j}| jj| j dd}|j}||k}|s\t d|fd||f¡dt ¡ v s+t |¡r0t 	|¡ndt 	|¡dt ¡ v s@t |¡rEt 	|¡nddœ }t 
d¡d	 d
|i }tt |¡ƒ‚d }}| j | j ¡}|j}||k}|s¶t d|fd||f¡dt ¡ v s…t |¡rŠt 	|¡ndt 	|¡dt ¡ v sšt |¡rŸt 	|¡nddœ }t 
d¡d	 d
|i }tt |¡ƒ‚d }}| j | j ¡}|j}||k}|st d|fd||f¡dt ¡ v sàt |¡råt 	|¡ndt 	|¡dt ¡ v sõt |¡rút 	|¡nddœ }t 
d¡d	 d
|i }tt |¡ƒ‚d }}dS )z8Verify stereo audio shape is preserved after processing.éx   ©Úcutoff_freq©ú==)z-%(py2)s
{%(py2)s = %(py0)s.shape
} == %(py4)sÚ	processedÚoriginal_shape)Úpy0Úpy2Úpy4zMono bass changed array shapez
>assert %(py6)sÚpy6NzExciter changed array shapez$Transient shaper changed array shape)r   Úshaper   Úmake_bass_monoÚ
@pytest_arÚ_call_reprcompareÚ@py_builtinsÚlocalsÚ_should_repr_global_nameÚ	_safereprÚ_format_assertmsgÚAssertionErrorÚ_format_explanationÚapply_spectral_exciterÚapply_transient_shaper)r   r)   r(   Ú@py_assert1Ú@py_assert3Ú@py_format5Ú@py_format7r    r    r!   Útest_stereo_shape_preserved&   s   ¦¦¬z1TestAudioEngineBasics.test_stereo_shape_preservedc                 C   s¾  t | jƒ}| j | j¡}t |ƒ}||k}|smt d|fd||f¡dt ¡ v s+t t ¡r0t 	t ¡nddt ¡ v s<t |¡rAt 	|¡ndt 	|¡dt ¡ v sQt |¡rVt 	|¡nddœ }t 
d¡d d	|i }tt |¡ƒ‚d
 }}| j | j¡}t |ƒ}||k}|sÙt d|fd||f¡dt ¡ v s—t t ¡rœt 	t ¡nddt ¡ v s¨t |¡r­t 	|¡ndt 	|¡dt ¡ v s½t |¡rÂt 	|¡nddœ }t 
d¡d d	|i }tt |¡ƒ‚d
 }}d
S )z#Verify mono audio can be processed.r&   )z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py5)sÚlenr(   Úoriginal_length)r*   Úpy1Úpy3Úpy5z$Transient shaper changed mono lengthz
>assert %(py7)sÚpy7NzExciter changed mono length)r@   r   r   r:   r0   r1   r2   r3   r4   r5   r6   r7   r8   r9   )r   rA   r(   Ú@py_assert2Ú@py_assert4Ú@py_format6Ú@py_format8r    r    r!   Ú test_mono_to_stereo_preservation6   s
   
ÊÎz6TestAudioEngineBasics.test_mono_to_stereo_preservationN)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r"   r?   rJ   r    r    r    r!   r      s
    r   c                   @   r   )	ÚTestMonoBassCorrectionz+Test mono bass correction (M/S processing).c                 C   ó   t dd| _d| j_dS ©r   r   r	   r   N©r   r   r   ©r   r    r    r!   r"   F   ó   z#TestMonoBassCorrection.setup_methodc                 C   sR  d}| j j}t d|t|| ƒ¡}t dtj d | ¡ tj¡}| }t 	||f¡}| j j
|dd}t |dd…df |dd…df  ¡ ¡ }t |dd…df |dd…df  ¡ ¡ }	|	|k }
|
s¥t d	|
fd
|	|f¡dt ¡ v sxt |	¡r}t |	¡nddt ¡ v s‰t |¡rŽt |¡nddœ }t d¡d d|i }tt |¡ƒ‚d}
dS )z9Low frequencies should become more mono after processing.çš™™™™™¹?r   r   é2   éÈ   r$   Né   ©ú<©z%(py0)s < %(py2)sÚprocessed_diffÚoriginal_diff©r*   r+   z8Low frequencies should become more mono after processingú
>assert %(py4)sr,   )r   r   r   r   r   r   r   r   r   r   r/   ÚabsÚmeanr0   r1   r2   r3   r4   r5   r6   r7   r8   )r   r   Úsrr   r   r   Ústereo_inputr(   r]   r\   r;   Ú@py_format3r=   r    r    r!   Útest_low_freq_becomes_monoK   sP    **ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿz1TestMonoBassCorrection.test_low_freq_becomes_monoc                 C   sH  d}| j j}t d|t|| ƒ¡}t dtj d | ¡ tj¡}t dtj d | tjd  ¡ tj¡}t 	||f¡}| j j
|dd}t t |d ¡¡}t t |d ¡¡}	t||	 ƒ| }
d}|
|k }|sžt d|fd	|
|f¡d
t ¡ v s~t |
¡rƒt |
¡nd
t |¡dœ }t d¡d d|i }tt |¡ƒ‚d }}dS )z)High frequencies should be less affected.rU   r   r   i'  é   éd   r$   rY   )z%(py0)s < %(py3)sÚrms_diff)r*   rC   z+High frequencies should be mostly preservedz
>assert %(py5)srD   N)r   r   r   r   r   r   r   r   r   r   r/   Úsqrtra   r`   r0   r1   r2   r3   r4   r5   r6   r7   r8   )r   r   rb   r   r   r   rc   r(   Úoriginal_rmsÚprocessed_rmsrh   rF   r;   Ú@py_format4rH   r    r    r!   Útest_high_freq_preservedc   s    *†z/TestMonoBassCorrection.test_high_freq_preservedN)rK   rL   rM   rN   r"   re   rm   r    r    r    r!   rO   C   s
    rO   c                   @   r   )	ÚTestSidechainCompressionz"Test auto-sidechain functionality.c                 C   rP   rQ   rR   rS   r    r    r!   r"      rT   z%TestSidechainCompression.setup_methodc              	   C   sR  | j j}d}t|| ƒ}tj|dftjdd }tj|dftjd}d|dt|d ƒ…dd…f< | j j||dd}t t 	|dt|d ƒ… d ¡¡}t t 	|t|d	 ƒd… d ¡¡}||k }	|	s¥t
 d
|	fd||f¡dt ¡ v sxt
 |¡r}t
 |¡nddt ¡ v s‰t
 |¡rŽt
 |¡nddœ }
t
 d¡d d|
i }tt
 |¡ƒ‚d}	dS )z(Bass should be reduced during kick hits.ç      à?r   ©ÚdtypegÍÌÌÌÌÌì?Ngš™™™™™©?çš™™™™™é?©Ústrengthgš™™™™™É?rY   r[   Úduring_kick_rmsÚafter_kick_rmsr^   z"Bass should be reduced during kickr_   r,   )r   r   r   r   Úonesr   ÚzerosÚapply_sidechainri   ra   r0   r1   r2   r3   r4   r5   r6   r7   r8   )r   rb   r   ÚsamplesÚbassÚkickr(   ru   rv   r;   rd   r=   r    r    r!   Útest_sidechain_reduces_level„   sP   $$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿz5TestSidechainCompression.test_sidechain_reduces_levelc                 C   s`   | j j}d}tj |d¡ tj¡}tj |d¡ tj¡}| j j||dd}tjj	||dd dS )z(Strength 0 should not affect the signal.iè  r   ç        rs   é   )ÚdecimalN)
r   r   r   ÚrandomÚrandnr   r   ry   ÚtestingÚassert_array_almost_equal)r   rb   rz   r{   r|   r(   r    r    r!   Útest_zero_strength_no_effect›   s   z5TestSidechainCompression.test_zero_strength_no_effectN)rK   rL   rM   rN   r"   r}   r…   r    r    r    r!   rn   |   s
    rn   c                   @   r   )	ÚTestTransientShaperz%Test transient shaping functionality.c                 C   rP   rQ   rR   rS   r    r    r!   r"   ¬   rT   z TestTransientShaper.setup_methodc           
      C   sì   | j j}t|d ƒ}tj |d¡ tj¡d }| j  |¡}|j	}|j	}||k}|snt
 d|fd||f¡dt ¡ v s>t
 |¡rCt
 |¡ndt
 |¡dt ¡ v sSt
 |¡rXt
 |¡ndt
 |¡dœ }dd	|i }	tt
 |	¡ƒ‚d
 } }}d
S )z Audio shape should be preserved.ro   r   r&   ©zH%(py2)s
{%(py2)s = %(py0)s.shape
} == %(py6)s
{%(py6)s = %(py4)s.shape
}r(   Úaudio©r*   r+   r,   r-   úassert %(py8)sÚpy8N)r   r   r   r   r   r‚   r   r   r:   r.   r0   r1   r2   r3   r4   r5   r7   r8   ©
r   rb   rz   rˆ   r(   r;   Ú@py_assert5r<   r>   Ú@py_format9r    r    r!   Útest_shape_preserved±   s
   ²z(TestTransientShaper.test_shape_preservedc           
      C   s&  | j j}t|d ƒ}tj|dftjd}d|ddd…f< t ddd	¡dd…df |d
d…dd…f< | j j|ddd}|j}|j}||k}|s‹t	 
d|fd||f¡dt ¡ v s[t	 |¡r`t	 |¡ndt	 |¡dt ¡ v spt	 |¡rut	 |¡ndt	 |¡dœ }dd|i }	tt	 |	¡ƒ‚d } }}dS )z-Attack boost should increase transient peaks.ro   r   rp   r   r   Nrr   rU   éc   rX   rg   g      @r~   )Úattack_boost_dbÚsustain_reduction_dbr&   r‡   r(   rˆ   r‰   rŠ   r‹   )r   r   r   r   rx   r   r   r:   r.   r0   r1   r2   r3   r4   r5   r7   r8   rŒ   r    r    r!   Ú!test_attack_boost_increases_peaks»   s   *ÿ²z5TestTransientShaper.test_attack_boost_increases_peaksN)rK   rL   rM   rN   r"   r   r“   r    r    r    r!   r†   ©   s
    
r†   c                   @   ó    e Zd ZdZdd„ Zdd„ ZdS )ÚTestSpectralExciterz$Test spectral exciter functionality.c                 C   rP   rQ   rR   rS   r    r    r!   r"   Ñ   rT   z TestSpectralExciter.setup_methodc                 C   sf  | j j}d}t d|t|| ƒ¡}t t dtj d | ¡ tj	¡t dtj d | ¡ tj	¡g¡}| j j
|dddd}t t |d ¡¡}t t |d ¡¡}tj}|||ƒ}	|	 }
|
s«t d¡d	 d
t ¡ v spt t¡rut t¡nd
t |¡dt ¡ v s…t |¡rŠt |¡nddt ¡ v s–t |¡r›t |¡ndt |	¡dœ }tt |¡ƒ‚d } }	}
dS )z$Exciter should add harmonic content.ro   r   r   i@  iˆ  iôÿÿÿ)Úhighpass_freqÚblend_dbÚdrivez Exciter should modify the signalzX
>assert not %(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.isclose
}(%(py3)s, %(py4)s)
}r   rj   rk   )r*   r+   rC   r,   r-   N)r   r   r   r   r   r   r   r   r   r   r9   ri   ra   Úiscloser0   r6   r2   r3   r4   r5   r7   r8   )r   rb   r   r   rˆ   r(   rj   rk   r;   r   Ú@py_assert7rI   r    r    r!   Útest_adds_harmonicsÖ   sr   þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿz'TestSpectralExciter.test_adds_harmonicsN)rK   rL   rM   rN   r"   r›   r    r    r    r!   r•   Î   ó    r•   c                   @   r   )	ÚTestPresetszTest preset configuration.c              	   C   sÊ   h d£}t  ¡ D ]Z\}}|D ]S}||v }|s_t d|fd||f¡dt ¡ v s+t |¡r0t |¡nddt ¡ v s<t |¡rAt |¡nddœ }t d|› d|› d	¡d
 d|i }t	t 
|¡ƒ‚d}qqdS )z8All presets should have all required configuration keys.>   Úmono_cutoff_hzZtransient_boost_dbÚtarget_lufsÚexciter_amountÚsidechain_strength)Úin)z%(py0)s in %(py2)sÚkeyÚpresetr^   úGenre 'z' missing key 'ú'r_   r,   N)r   Úitemsr0   r1   r2   r3   r4   r5   r6   r7   r8   )r   Úrequired_keysÚgenrer¤   r£   r;   rd   r=   r    r    r!   Ú"test_all_genres_have_required_keysò   s   ¤ÿÿz.TestPresets.test_all_genres_have_required_keysc              	   C   sÚ   t  ¡ D ]f\}}|d }d}| }||k}d}||k}|r|s`t d||fd|||f¡t |¡dt ¡ v s:t |¡r?t |¡ndt |¡dœ }	t d|› d	|› ¡d
 d|	i }
t	t 
|
¡ƒ‚d } } } }}qdS )z*LUFS values should be in reasonable range.rŸ   é   r   ©ú<=r­   )z-%(py1)s <= %(py5)sz%(py5)s <= %(py7)sÚlufs)rB   rD   rE   r¥   z' has unreasonable LUFS: z
>assert %(py9)sÚpy9N©r   r§   r0   r1   r5   r2   r3   r4   r6   r7   r8   )r   r©   r¤   r®   Ú@py_assert0rF   r<   Ú@py_assert6rG   rI   Ú@py_format10r    r    r!   Útest_lufs_values_reasonable   s   ¾þz'TestPresets.test_lufs_values_reasonablec           
   	   C   sÐ   t  ¡ D ]a\}}|d }d}||k}d}||k}|r|s]t d||fd|||f¡t |¡dt ¡ v s7t |¡r<t |¡ndt |¡dœ }t d|› d	|› ¡d
 d|i }	t	t 
|	¡ƒ‚d } } }}qdS )z!Sidechain strength should be 0-1.r¡   r   rX   r¬   )z%(py1)s <= %(py4)sz%(py4)s <= %(py6)srt   )rB   r,   r-   r¥   z"' has invalid sidechain strength: z
>assert %(py8)sr‹   Nr°   )
r   r©   r¤   rt   r±   rF   r   r<   r>   rŽ   r    r    r!   Ú"test_sidechain_strength_normalized  s@   "ÿÿÿÿÿÿÿÿÿÿ
ÿÿÿÿþz.TestPresets.test_sidechain_strength_normalizedN)rK   rL   rM   rN   rª   r´   rµ   r    r    r    r!   r   ï   s
    r   c                   @   r”   )ÚTestLoudnessNormalizationz*Test loudness normalization functionality.c                 C   rP   rQ   rR   rS   r    r    r!   r"     rT   z&TestLoudnessNormalization.setup_methodc                 C   s@  | j j}d}t d|t|| ƒ¡}t t dtj d | ¡ tj	¡d t dtj d | ¡ tj	¡d g¡}| j j
|dd}t t |d ¡¡}t t |d ¡¡}||k}|sœt d|fd	||f¡d
t ¡ v sot |¡rtt |¡nd
dt ¡ v s€t |¡r…t |¡nddœ }	t d¡d d|	i }
tt |
¡ƒ‚d}dS )z(Normalization should adjust audio level.r   r   r   r   rU   g       À)rŸ   )ú>)z%(py0)s > %(py2)srk   rj   r^   z/Normalization should increase quiet audio levelr_   r,   N)r   r   r   r   r   r   r   r   r   r   Únormalize_loudnessri   ra   r0   r1   r2   r3   r4   r5   r6   r7   r8   )r   rb   r   r   rˆ   r(   rj   rk   r;   rd   r=   r    r    r!   Ú test_normalization_changes_level  sR   ""þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿz:TestLoudnessNormalization.test_normalization_changes_levelN)rK   rL   rM   rN   r"   r¹   r    r    r    r!   r¶     rœ   r¶   c                  C   sR  t dƒ t dƒ t dƒ tttttttg} d}d}| D ]v}t d|j› dƒ |ƒ }t	|ƒD ]c}| 
d¡rzt|dƒr>| ¡  t||ƒƒ  t d|› ƒ |d	7 }W q- typ } zt d
|› d|› ƒ |d	7 }W Y d}~q-d}~w ty } zt d
|› d|› ƒ |d	7 }W Y d}~q-d}~ww q-qt dƒ t d|› d|› dƒ t dƒ |dkS )z Run all tests and print results.z<============================================================zSunoRemaster DSP Unit Testsr   Ú
ú:Útest_r"   u     âœ“ rX   u     âœ— z: Nz: Exception - z=
============================================================z	Results: z	 passed, z failed)Úprintr   rO   rn   r†   r•   r   r¶   rK   ÚdirÚ
startswithÚhasattrr"   Úgetattrr7   Ú	Exception)Ztest_classesÚpassedÚfailedZ
test_classÚinstanceÚmethod_nameÚer    r    r!   Ú	run_tests,  sN   ù


€€þõÿrÈ   Ú__main__rX   ) rN   Úbuiltinsr2   Ú_pytest.assertion.rewriteÚ	assertionÚrewriter0   ÚsysÚosÚpathÚinsertÚjoinÚdirnameÚ__file__Únumpyr   ÚpytestÚengine.processorr   Úengine.presetsr   r   rO   rn   r†   r•   r   r¶   rÈ   rK   ÚsuccessÚexitr    r    r    r!   Ú<module>   s(    ""-9-%!/þ