4141from typing import IO , TYPE_CHECKING , Any , Callable , Generic , TypeVar
4242
4343from .errors import ClientException
44+ from .enums import SpeakingState
4445from .oggparse import OggStream
45- from .opus import Encoder as OpusEncoder
46+ from .opus import Encoder as OpusEncoder , OPUS_SILENCE
4647from .utils import MISSING
4748
4849if TYPE_CHECKING :
49- from .voice_client import VoiceClient
50+ from .voice import VoiceClient
5051
5152
5253AT = TypeVar ("AT" , bound = "AudioSource" )
@@ -732,7 +733,6 @@ def __init__(self, source: AudioSource, client: VoiceClient, *, after=None):
732733 self ._resumed : threading .Event = threading .Event ()
733734 self ._resumed .set () # we are not paused
734735 self ._current_error : Exception | None = None
735- self ._connected : threading .Event = client ._connected
736736 self ._lock : threading .Lock = threading .Lock ()
737737 self ._played_frames_offset : int = 0
738738
@@ -742,58 +742,60 @@ def __init__(self, source: AudioSource, client: VoiceClient, *, after=None):
742742 def _do_run (self ) -> None :
743743 # attempt to read first audio segment from source before starting
744744 # some sources can take a few seconds and may cause problems
745- first_data = self .source .read ()
746745 self .loops = 0
747746 self ._start = time .perf_counter ()
748747
749748 # getattr lookup speed ups
750- play_audio = self .client .send_audio_packet
751- self ._speak (True )
749+ client = self .client
750+ play_audio = client .send_audio_packet
751+ self ._speak (SpeakingState .voice )
752752
753753 while not self ._end .is_set ():
754754 # are we paused?
755755 if not self ._resumed .is_set ():
756+ self .send_silence ()
756757 # wait until we aren't
757758 self ._resumed .wait ()
758759 continue
759760
760- # are we disconnected from voice?
761- if not self ._connected .is_set ():
762- # wait until we are connected
763- self ._connected .wait ()
764- # reset our internal data
765- self ._played_frames_offset += self .loops
766- self .loops = 0
767- self ._start = time .perf_counter ()
768-
769- self .loops += 1
770-
771- # Send the data read from the start of the function if it is not None
772- if first_data is not None :
773- data = first_data
774- first_data = None
775- # Else read the next bit from the source
776- else :
777- data = self .source .read ()
761+ data = self .source .read ()
778762
779763 if not data :
780764 self .stop ()
781765 break
782766
767+ # are we disconnected from voice?
768+ if not client .is_connected ():
769+ _log .debug ('Not connected, waiting for %ss...' , client .timeout )
770+ # wait until we are connected, but not forever
771+ connected = client .wait_until_connected (client .timeout )
772+ if self ._end .is_set () or not connected :
773+ _log .debug ('Aborting playback' )
774+ return
775+ _log .debug ('Reconnected, resuming playback' )
776+ self ._speak (SpeakingState .voice )
777+ # reset our internal data
778+ self .loops = 0
779+ self ._start = time .perf_counter ()
780+
783781 play_audio (data , encode = not self .source .is_opus ())
782+ self .loops += 1
784783 next_time = self ._start + self .DELAY * self .loops
785784 delay = max (0 , self .DELAY + (next_time - time .perf_counter ()))
786785 time .sleep (delay )
787786
787+ if client .is_connected ():
788+ self .send_silence ()
789+
788790 def run (self ) -> None :
789791 try :
790792 self ._do_run ()
791793 except Exception as exc :
792794 self ._current_error = exc
793795 self .stop ()
794796 finally :
795- self .source .cleanup ()
796797 self ._call_after ()
798+ self .source .cleanup ()
797799
798800 def _call_after (self ) -> None :
799801 error = self ._current_error
@@ -802,32 +804,29 @@ def _call_after(self) -> None:
802804 try :
803805 self .after (error )
804806 except Exception as exc :
805- _log .exception ("Calling the after function failed." )
806807 exc .__context__ = error
807- traceback . print_exception ( type ( exc ), exc , exc . __traceback__ )
808+ _log . exception ( "Calling the after function failed." , exc_info = exc )
808809 elif error :
809810 msg = f"Exception in voice thread { self .name } "
810811 _log .exception (msg , exc_info = error )
811- print (msg , file = sys .stderr )
812- traceback .print_exception (type (error ), error , error .__traceback__ )
813812
814813 def stop (self ) -> None :
815814 self ._end .set ()
816815 self ._resumed .set ()
817- self ._speak (False )
816+ self ._speak (SpeakingState . none )
818817
819818 def pause (self , * , update_speaking : bool = True ) -> None :
820819 self ._resumed .clear ()
821820 if update_speaking :
822- self ._speak (False )
821+ self ._speak (SpeakingState . none )
823822
824823 def resume (self , * , update_speaking : bool = True ) -> None :
825824 self ._played_frames_offset += self .loops
826825 self .loops = 0
827826 self ._start = time .perf_counter ()
828827 self ._resumed .set ()
829828 if update_speaking :
830- self ._speak (True )
829+ self ._speak (SpeakingState . voice )
831830
832831 def is_playing (self ) -> bool :
833832 return self ._resumed .is_set () and not self ._end .is_set ()
@@ -841,14 +840,21 @@ def _set_source(self, source: AudioSource) -> None:
841840 self .source = source
842841 self .resume (update_speaking = False )
843842
844- def _speak (self , speaking : bool ) -> None :
843+ def _speak (self , state : SpeakingState ) -> None :
845844 try :
846845 asyncio .run_coroutine_threadsafe (
847- self .client .ws .speak (speaking ), self .client .loop
846+ self .client .ws .speak (state ), self .client .loop
848847 )
849848 except Exception as e :
850849 _log .info ("Speaking call in player failed: %s" , e )
851850
852851 def played_frames (self ) -> int :
853852 """Gets the number of 20ms frames played since the start of the audio file."""
854853 return self ._played_frames_offset + self .loops
854+
855+ def send_silence (self , count : int = 5 ) -> None :
856+ try :
857+ for n in range (count ):
858+ self .client .send_audio_packet (OPUS_SILENCE , encode = False )
859+ except Exception :
860+ pass
0 commit comments