
    Gipg                         d Z ddlZddlZddlZddlZddlmZ  ej                  e      Z	 G d d      Z
 G d d      Zy)z2Sound and music manager for Play Palace v9 client.    N)SoundCacherc                   J    e Zd ZdZ	 	 	 	 ddZd Zd Zd Zd Zd Z	d Z
d	 Zy
)AudioPlaylistzBRepresents a playlist that can play either sounds or music tracks.c                    ddl }|j                         | _        |j                         | _        || _        || _        || _        |dk(  r|nt        d|      | _        d| _	        d| _
        d| _        d| _        d| _        d| _        || _        d| _        |r|j                  | j                         |r%| j                  rd| _        | j#                          yyy)aX  
        Initialize an audio playlist.

        Args:
            tracks: List of audio file names to play
            audio_type: Either "sound" or "music" to determine playback method
            sound_manager: Reference to parent SoundManager
            shuffle: If True, shuffle the tracks randomly
            repeats: Number of times to repeat the playlist (0 for infinite, minimum 1 otherwise)
            auto_start: If True, automatically start playing the first track
            auto_remove: If True, automatically remove playlist when all repeats complete (ignored for infinite)
        r   N   FT)randomcopyoriginal_trackstracks
audio_typesound_managershufflemaxrepeatscurrent_repeattrack_index	is_activesync_handlecallbackcurrent_streamauto_removeplaylist_id_play_next_track)	selfr   r   r   r   r   
auto_startr   r   s	            <C:\Users\dbart\PlayPalace11\clients\desktop\sound_manager.py__init__zAudioPlaylist.__init__   s    , 	%{{}kkm$*!|GQ 	  "& NN4;;' $++!DN!!# &:    c                    | j                   r| j                  sy| j                  t        | j                        k\  rd| _        | xj                  dz  c_        | j
                  dk7  rg| j                  | j
                  kD  rN| j                          | j                  r1| j                  r%| j                  j                  | j                         y| j                  A| j                  r5	 ddlm}  || j                  j                  | j                         d| _        | j                  | j                     }| j*                  dk(  r:| j                  j-                  |dd       | j                  j.                  | _        ndd	lm} t4        j6                  j9                  | j                  j:                  |      }|| j                  j<                  j>                  vrYtA        |d
      5 }ddl!}|jE                  |jG                               | j                  j<                  j>                  |<   ddd       | j                  j<                  j>                  |   }|jI                  d|t        |            | _        | j                  rQ	 ddlm%}	m&}
m'}  || jP                        | _)         |	| j                  j                  |
d| jR                  d      | _        | j                  rb| j*                  dk(  rS| j                  j[                          | j                  j<                  j\                  j_                  | j                         | xj                  dz  c_        y# t         t"        t$        f$ r!}t&        j)                  d|       Y d}~td}~ww xY w# 1 sw Y   hxY w# tT        $ r ddl+}|jY                          d| _        Y w xY w)z$Play the next track in the playlist.Nr   r   BASS_ChannelRemoveSyncFailed to remove audio sync: %smusicF)loopingfade_out_oldstreamrbTmemfilelength)BASS_ChannelSetSyncBASS_SYNC_ENDSYNCPROCsound)0r   r   r   lenr   r   stopr   r   r   remove_playlistr   r   sound_lib.external.pybassr!   handleAttributeErrorOSErrorRuntimeErrorLOGdebugr   r#   current_music	sound_libr'   ospathjoinsounds_foldersound_cachercacheopenctypescreate_string_bufferread
FileStreamr-   r.   r/   _on_track_end_callbackr   	Exception	traceback	print_excplayrefsappend)r   r!   exctrackr'   
track_pathfrD   cache_bufferr-   r.   r/   rJ   s                r   r   zAudioPlaylist._play_next_trackA   s   ~~T[[ s4;;// D1$ ||q T%8%84<<%G		##(8(8&&66t7G7GH 'D,?,?BL&t':':'A'A4CSCST  $D D,,- ??g%$$UE$N"&"4"4"B"BD )d&8&8&F&FNJ D..;;AAA*d+q!CIC^C^DD&&3399%@ ,  --::@@GL"("3"3|C4E #4 #D
 (  !))D)D E $7''..q$--QU$  4??g#=$$&++00778K8KL 	Ay #G\: B		;SAAB& ,+:  ( ##%#' 	(s8   '-M %ANAN N,NNN$OOc                 X    | j                   r| j                  r| j                          yyy)z?BASS callback function triggered when a track finishes playing.N)r   r   r   )r   r5   channeldatausers        r   rH   z$AudioPlaylist._on_track_end_callback   s"    >>dkk!!# *>r   c                 ,   d| _         | j                  J| j                  r=	 ddlm}  || j                  j
                  | j                         d| _        d| _        yyy# t        t        t        f$ r }t        j                  d|       Y d}~@d}~ww xY w)zStop the playlist.FNr   r    r"   )r   r   r   r4   r!   r5   r6   r7   r8   r9   r:   r   )r   r!   rO   s      r   r2   zAudioPlaylist.stop   s     'D,?,?BL&t':':'A'A4CSCST  $D DM -@'
 #G\: B		;SAABs   -A B3BBc                    	 t         j                  j                  | j                  j                  |      }ddl}ddlm} || j                  j                  j                  vrUt        |d      5 }|j                  |j                               | j                  j                  j                  |<   ddd       |j                  d| j                  j                  j                  |   t        | j                  j                  j                  |               }|j                  }|j!                          |S # 1 sw Y   xY w# t"        $ r ddl}|j'                          Y yw xY w)z
        Get the duration of a single track in seconds.

        Args:
            track: Track filename

        Returns:
            Duration in seconds, or 0 if unable to determine
        r   Nr&   r(   Tr)   )r=   r>   r?   r   r@   rD   r<   r'   rA   rB   rC   rE   rF   rG   r1   r,   freerI   rJ   rK   )	r   rP   rQ   rD   sound_streamrR   temp_streamdurationrJ   s	            r   _get_track_durationz!AudioPlaylist._get_track_duration   s+    	d&8&8&F&FNJ 8 !3!3!@!@!F!FF*d+qHNHcHcID&&3399*E , '11''44:::F4--::@@LM 2 K #))H O% ,+&  	!		s,   A,E  .AD4/BE  4D=9E   E E c                     	 d}| j                   D ]  }|| j                  |      z  } t        |dz        S # t        $ r ddl}|j                          Y yw xY w)z
        Get the total duration of all tracks in the playlist (in milliseconds).

        Returns:
            Total duration in milliseconds, or None if unable to calculate
        r     N)r   r^   intrI   rJ   rK   )r   total_duration_secondsrP   rJ   s       r   get_total_durationz AudioPlaylist.get_total_duration   se    
	%&"&$*B*B5*II& % -455 	!		s   47 AAc                    | j                   sy	 d}| j                  dkD  r| j                  dz
  nd}t        |      D ]#  }|| j                  | j                  |         z  }% | j
                  r0t        | j
                  d      r	 || j
                  j                  z  }t        |dz        S # t        t        t        f$ r }t        j                  d|       Y d}~=d}~ww xY w# t        $ r ddl}|j!                          Y yw xY w)a=  
        Get the elapsed duration of the current iteration (in milliseconds).

        This includes:
        - The full duration of all completed tracks
        - The current position in the currently playing track (if any)

        Returns:
            Elapsed duration in milliseconds, or 0 if not playing
        r   r   positionz*Failed to read current stream position: %sNr`   )r   r   ranger^   r   r   hasattrre   r6   r7   r8   r9   r:   ra   rI   rJ   rK   )r   elapsed_secondscurrent_playing_indexirO   rJ   s         r   get_elapsed_durationz"AudioPlaylist.get_elapsed_duration   s    ~~	O =A<L<Lq<PD$4$4q$8VW! 014#;#;DKKN#KK 2 ""wt/B/BJ'OQ#t':':'C'CCO -.. '> QIIJCPPQ  	!		s<   A5C# B, C# ,C  CC# C  C# #DDc                    | j                   sy	 d}| j                  dkD  r| j                  dz
  nd}| j                  ra	 | j                  | j                  |         }t        | j                  d      r| j                  j                  nd}t        d||z
        }||z  }t        |dz   t        | j                              D ]#  }|| j                  | j                  |         z  }% t        |dz        S # t        t        t        f$ r }t        j                  d|       Y d}~d}~ww xY w# t         $ r ddl}|j%                          Y yw xY w)a?  
        Get the remaining duration of the current iteration (in milliseconds).

        This includes:
        - The remaining time in the currently playing track (if any)
        - The full duration of all remaining tracks

        Returns:
            Remaining duration in milliseconds, or 0 if not playing
        r   r   re   z(Failed to compute remaining duration: %sNr`   )r   r   r   r^   r   rg   re   r   r6   r7   r8   r9   r:   rf   r1   ra   rI   rJ   rK   )	r   remaining_secondsri   current_track_durationcurrent_positionremaining_in_currentrO   rj   rJ   s	            r   get_remaining_durationz$AudioPlaylist.get_remaining_duration  s\    ~~	 ! =A<L<Lq<PD$4$4q$8VW! ""O-1-E-E$9:.*
 #4#6#6
C ++44 %
 ,/q2HK[2[+\(%)==%
 014c$++6FG!T%=%=dkk!n%MM! H (4/00 '> OIIH#NNO  	!		s<   .D+ A C4 AD+ 4D(D#D+ #D((D+ +E
EN)Fr   TT)__name__
__module____qualname____doc__r   r   rH   r2   r^   rc   rk   rq    r   r   r   r      s@    L 0$dTl$
!*X&$L-r   r   c                       e Zd ZdZd ZddZddededefdZd Z	d	 Z
d
 ZddZd Zd Zd Zd Zd Zd ZddZd Z	 	 	 	 	 ddZd Zd Zd Zy)SoundManagerz4Manages sound effects and background music playback.c                     t               | _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _	        d| _
        d| _        d| _        d| _        d| _        i | _        y)zInitialize the sound manager.Ng?soundszmenuclick.oggzmenuenter.oggg333333?F)r   rA   r;   current_music_namemusic_volumer@   menuclick_soundmenuenter_soundfade_threadambience_introambience_loopambience_outroambience_volumeambience_threadambience_stop_flag	playlistsr   s    r   r   zSoundManager.__init__C  s    'M!"&%  /.   #!""#"' r   c                     t         j                  j                  | j                  |      }	 | j                  j                  ||||      S # t        $ r Y yw xY w)aD  
        Play a sound effect.

        Args:
            sound_name: Name of sound file (assumed to be in sounds/ folder)
            volume: Volume level 0.0-1.0
            pan: Pan -1.0 (left) to 1.0 (right)
            pitch: Pitch multiplier (1.0 = normal)

        Returns:
            The sound stream object
        )panvolumepitchN)r=   r>   r?   r@   rA   rL   rI   )r   
sound_namer   r   r   
sound_paths         r   rL   zSoundManager.play]  sV     WW\\$"4"4jA
	$$))*#fTY)ZZ 		s   A 	AA
music_namer$   r%   c                 P   | j                   |k(  r| j                  ry| j                  r	 | j                  j                          t        j                  j                  | j                  |      }	 | j                  j                  || j                        | _        | j                  r|| j                  _        || _         y# t        t        t
        f$ r }t        j                  d|       Y d}~d}~ww xY w# t         $ r% ddl}|j%                          d| _        d| _         Y yw xY w)aD  
        Play background music with looping.

        Args:
            music_name: Name of music file (assumed to be in sounds/ folder)
            looping: whether to loop the music track or not.
            fade_out_old: Whether to fade out the current music before starting new (ignored, kept for compatibility)
        Nz Failed to stop current music: %sr   r   )r{   r;   r2   r6   r7   r8   r9   r:   r=   r>   r?   r@   rA   rL   r|   r$   rI   rJ   rK   )r   r   r$   r%   rO   
music_pathrJ   s          r   r#   zSoundManager.musicr  s     ""j0T5G5G C""'')
 WW\\$"4"4jA

	+!%!2!2!7!7
4K\K\!7!]D!!-4""*&0D# #G\: C		<cBBC  	+!!%D&*D#	+s*   C  /AC7  C4C//C47+D%$D%c                 ^    fd}t        j                  |d      }|j                          y)z*Fade out old music in a background thread.c                     	 j                   } d}t        |dd      D ]$  }| ||z  z  _         t        j                  d       & j	                          y # t
        t        t        f$ r }t        j                  d|       Y d }~y d }~ww xY wN   皙?Failed to fade old music: %s
r   rf   timesleepr2   r6   r7   r8   r9   r:   start_volumestepsrj   rO   	old_musics       r   fadez5SoundManager._fade_out_old_music_thread.<locals>.fade  }    ?(//ub"-A'3q5y'AI$JJt$ .  "G\: ?		8#>>?   AA B
*BB
TtargetdaemonN)	threadingThreadstart)r   r   r   r   s    `  r   _fade_out_old_music_threadz'SoundManager._fade_out_old_music_thread  s(    		?  &&d4@r   c                 J   | j                   sy| j                   }	 |j                  }d}t        |dd      D ]$  }|||z  z  |_        t        j                  d       & |j                          y# t        t        t        f$ r }t        j                  d|       Y d}~yd}~ww xY w)z7Fade out the current music and wait for it to complete.Nr   r   r   r   )r;   r   rf   r   r   r2   r6   r7   r8   r9   r:   )r   r   r   r   rj   rO   s         r   _fade_out_music_blockingz%SoundManager._fade_out_music_blocking  s    !!&&		;$++LE5"b)#/1u9#=	 

4  * NN6 	;II4c::	;s   AA. .B"BB"c                     | j                   sy| j                   fd}t        j                  |d      }|j                          y)z2Fade out the current music in a background thread.Nc                     	 j                   } d}t        |dd      D ]$  }| ||z  z  _         t        j                  d       & j	                          y # t
        t        t        f$ r }t        j                  d|       Y d }~y d }~ww xY wr   r   r   s       r   r   z*SoundManager._fade_out_music.<locals>.fade  r   r   Tr   )r;   r   r   r   )r   r   r   r   s      @r   _fade_out_musiczSoundManager._fade_out_music  s?    !!&&			?  &&d4@r   c                    | j                   r=|r| j                          n	 | j                   j                          d| _         d| _        yy# t        t        t
        f$ r }t        j                  d|       Y d}~?d}~ww xY w)zn
        Stop the current music.

        Args:
            fade: Whether to fade out before stopping
        zFailed to stop music: %sN)	r;   r   r2   r6   r7   r8   r9   r:   r{   )r   r   rO   s      r   
stop_musiczSoundManager.stop_music  su     $$&?&&++- "&D&*D#  '> ?II8#>>?s   A A?A::A?c                     t        dt        d|            | _        | j                  r	 | j                  | j                  _        yy# t
        t        t        f$ r }t        j                  d|       Y d}~yd}~ww xY w)z_
        Set the music volume.

        Args:
            volume: Volume level 0.0-1.0
                      ?zFailed to set music volume: %sN)
r   minr|   r;   r   r6   r7   r8   r9   r:   r   r   rO   s      r   set_music_volumezSoundManager.set_music_volume  so      Sf%56A,0,=,="")  #G\: A		:C@@As   A A:A55A:c                 >    | j                  | j                  d       y)zPlay the menu click sound.      ?r   N)rL   r}   r   s    r   play_menuclickzSoundManager.play_menuclick      		$&&s	3r   c                 >    | j                  | j                  d       y)z#Play the menu enter/activate sound.r   r   N)rL   r~   r   s    r   play_menuenterzSoundManager.play_menuenter  r   r   c                     || _         y)z
        Set the menu click sound (called by server).

        Args:
            sound_name: Name of sound file in sounds/ folder
        N)r}   r   r   s     r   set_menuclick_soundz SoundManager.set_menuclick_sound        *r   c                     || _         y)z
        Set the menu enter/activate sound (called by server).

        Args:
            sound_name: Name of sound file in sounds/ folder
        N)r~   r   s     r   set_menuenter_soundz SoundManager.set_menuenter_sound  r   r   c                       j                  d        fd}d _        t        j                  |d       _         j                  j                          y)a#  
        Play ambience with intro, loop, and outro.

        Args:
            intro_name: Name of intro sound file (or None to skip)
            loop_name: Name of loop sound file (required, will loop continuously)
            outro_name: Name of outro sound file (or None to skip)
        T)forcec                     	 ddl m}  | 	 d _        d _        d _        y r*t
        j                  j                  j                        nd }t
        j                  j                  j                  	      }
r*t
        j                  j                  j                  
      nd }|rj                   j                  |j                        _        j                  r}j                  j                  rDj                  s8t        j                  d       j                  j                  rj                  s8j                  r	 d _        d _        d _        y dd l}ddlm} |j                   j$                  vrKt'        |d      5 }|j)                  |j+                               j                   j$                  |<   d d d        |j-                  dj                   j$                  |   t/        j                   j$                  |               _        j                  j                  _        dj                  _        j                  j                          j                   j4                  j7                  j                         j                  rՉj                  s"t        j                  d       j                  s"d	j                  _        j                  j9                          |rzj                   j                  |j                        _        j                  rBj                  j                  r,t        j                  d       j                  j                  r,d _        d _        d _        y # 1 sw Y   xY w# t:        $ r dd l}|j?                          Y Bw xY w# d _        d _        d _        w xY w)
Nr   )or   g?r&   r(   Tr)   F) rA   r   r   r   r   r=   r>   r?   r@   rL   r   
is_playingr   r   r   rD   r<   r'   rB   rC   rE   rF   rG   r1   r   r$   rM   rN   r2   rI   rJ   rK   )r   
intro_path	loop_path
outro_pathrD   r[   rR   rJ   
intro_name	loop_name
outro_namer   s           r   play_ambience_sequencez5SoundManager.ambience.<locals>.play_ambience_sequence  s1   G+*9~ '+#%)"&*# NXRWW\\$*<*<jI]a
GGLL););YG	MWRWW\\$*<*<jI]a
 *.*;*;*@*@"4+?+? +A +D' **"11<<TE\E\ JJsO #11<<TE\E\22"^ '+#%)"&*#[ < D$5$5$;$;;i.!=C=X=XYZY_Y_Ya=b))//	: / &2%<%<**00;t0066yAB &= &"
 -1,@,@"")-1""*""'')!!&&--d.@.@A%%"55

3 #55 27D&&.&&++- ".2.?.?.D.D&t/C/C /E /+  .."&"5"5"@"@ $

3 #'"5"5"@"@ '+#%)"&*#Q /.B  & ##%&
 '+#%)"&*#sU   N DN <N .N 7NDN B&N NN N52N8 4N55N8 8OFr   N)stop_ambiencer   r   r   r   r   )r   r   r   r   r   s   ```` r   ambiencezSoundManager.ambience  sP     	&H	+V #((//7MVZ[""$r   c                 v   d| _         |r| j                  r	 | j                  j                          | j                  r	 | j                  j                          | j                  r	 | j                  j                          d| _        d| _        d| _	        yy# t        t        t
        f$ r }t        j                  d|       Y d}~d}~ww xY w# t        t        t
        f$ r }t        j                  d|       Y d}~d}~ww xY w# t        t        t
        f$ r }t        j                  d|       Y d}~d}~ww xY w)z
        Stop the current ambience.

        Args:
            force: If True, immediately stop all sounds without playing outro.
                   If False, let the outro play naturally.
        Tz!Failed to stop ambience intro: %sNz Failed to stop ambience loop: %sz!Failed to stop ambience outro: %s)
r   r   r2   r6   r7   r8   r9   r:   r   r   )r   r   rO   s      r   r   zSoundManager.stop_ambiencec  s    #'""H'',,. !!G&&++- ""H'',,. #'D!%D"&D) 
 '> HIIA3GGH
 '> GII@#FFG
 '> HIIA3GGHsF   B C %D C
*CC
D!C<<DD8D33D8c                 v   t        dt        d|            | _        | j                  r	 | j                  | j                  _        | j                  r	 | j                  | j                  _        | j                  r	 | j                  | j                  _        yy# t
        t        t        f$ r }t        j                  d|       Y d}~d}~ww xY w# t
        t        t        f$ r }t        j                  d|       Y d}~d}~ww xY w# t
        t        t        f$ r }t        j                  d|       Y d}~yd}~ww xY w)zb
        Set the ambience volume.

        Args:
            volume: Volume level 0.0-1.0
        r   r   z'Failed to set ambience intro volume: %sNz&Failed to set ambience loop volume: %sz'Failed to set ambience outro volume: %s)r   r   r   r   r   r6   r7   r8   r9   r:   r   r   r   s      r   set_ambience_volumez SoundManager.set_ambience_volume  s     #3C(89 J-1-A-A##* I,0,@,@"") J-1-A-A##*  #G\: J		CSIIJ
 #G\: I		BCHHI
 #G\: J		CSIIJsG   B C 9D C
*CC
D!C<<DD8D33D8c           	          || j                   v r| j                  |       t        ||| ||||      }||_        || j                   |<   y)aU  
        Add a new audio playlist.

        Args:
            playlist_id: Unique identifier for this playlist
            tracks: List of audio file names to play
            audio_type: Either "sound" or "music" to determine playback method
            shuffle: If True, shuffle the tracks randomly
            repeats: Number of times to repeat the playlist (0 for infinite, minimum 1 otherwise)
            auto_start: If True, automatically start playing the first track
            auto_remove: If True, automatically remove playlist when all repeats complete (ignored for infinite)
        N)r   r3   r   r   )	r   r   r   r   r   r   r   r   playlists	            r   add_playlistzSoundManager.add_playlist  sQ    . $..(  - !Jgw
K
  +&.{#r   c                 z    || j                   v r-| j                   |   }|j                          | j                   |= yy)z
        Remove and stop a playlist.

        Args:
            playlist_id: Unique identifier of the playlist to remove
        N)r   r2   )r   r   r   s      r   r3   zSoundManager.remove_playlist  s8     $..(~~k2HMMO{+ )r   c                 z    t        | j                  j                               }|D ]  }| j                  |        y)z0
        Remove and stop all playlists.
        N)listr   keysr3   )r   playlist_idsr   s      r   remove_all_playlistsz!SoundManager.remove_all_playlists  s3    
 DNN//12'K  - (r   c                 8    | j                   j                  |      S )z
        Get a playlist by ID.

        Args:
            playlist_id: Unique identifier of the playlist

        Returns:
            AudioPlaylist object or None if not found
        )r   get)r   r   s     r   get_playlistzSoundManager.get_playlist  s     ~~!!+..r   N)r   r   r   )TT)T)F)r#   Fr   TT)rr   rs   rt   ru   r   rL   strboolr#   r   r   r   r   r   r   r   r   r   r   r   r   r   r3   r   r   rv   r   r   rx   rx   @  s    >4* +  +d  +  +D$;",+$A44**Z%x'DJ< /B
,.
/r   rx   )ru   loggingr=   r   r   rA   r   	getLoggerrr   r9   r   rx   rv   r   r   <module>r      sB    8  	   $g!q qh	_/ _/r   