41
41
from typing import IO , TYPE_CHECKING , Any , Callable , Generic , TypeVar
42
42
43
43
from .errors import ClientException
44
+ from .enums import SpeakingState
44
45
from .oggparse import OggStream
45
- from .opus import Encoder as OpusEncoder
46
+ from .opus import Encoder as OpusEncoder , OPUS_SILENCE
46
47
from .utils import MISSING
47
48
48
49
if TYPE_CHECKING :
49
- from .voice_client import VoiceClient
50
+ from .voice import VoiceClient
50
51
51
52
52
53
AT = TypeVar ("AT" , bound = "AudioSource" )
@@ -732,7 +733,6 @@ def __init__(self, source: AudioSource, client: VoiceClient, *, after=None):
732
733
self ._resumed : threading .Event = threading .Event ()
733
734
self ._resumed .set () # we are not paused
734
735
self ._current_error : Exception | None = None
735
- self ._connected : threading .Event = client ._connected
736
736
self ._lock : threading .Lock = threading .Lock ()
737
737
self ._played_frames_offset : int = 0
738
738
@@ -742,58 +742,60 @@ def __init__(self, source: AudioSource, client: VoiceClient, *, after=None):
742
742
def _do_run (self ) -> None :
743
743
# attempt to read first audio segment from source before starting
744
744
# some sources can take a few seconds and may cause problems
745
- first_data = self .source .read ()
746
745
self .loops = 0
747
746
self ._start = time .perf_counter ()
748
747
749
748
# 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 )
752
752
753
753
while not self ._end .is_set ():
754
754
# are we paused?
755
755
if not self ._resumed .is_set ():
756
+ self .send_silence ()
756
757
# wait until we aren't
757
758
self ._resumed .wait ()
758
759
continue
759
760
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 ()
778
762
779
763
if not data :
780
764
self .stop ()
781
765
break
782
766
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
+
783
781
play_audio (data , encode = not self .source .is_opus ())
782
+ self .loops += 1
784
783
next_time = self ._start + self .DELAY * self .loops
785
784
delay = max (0 , self .DELAY + (next_time - time .perf_counter ()))
786
785
time .sleep (delay )
787
786
787
+ if client .is_connected ():
788
+ self .send_silence ()
789
+
788
790
def run (self ) -> None :
789
791
try :
790
792
self ._do_run ()
791
793
except Exception as exc :
792
794
self ._current_error = exc
793
795
self .stop ()
794
796
finally :
795
- self .source .cleanup ()
796
797
self ._call_after ()
798
+ self .source .cleanup ()
797
799
798
800
def _call_after (self ) -> None :
799
801
error = self ._current_error
@@ -802,32 +804,29 @@ def _call_after(self) -> None:
802
804
try :
803
805
self .after (error )
804
806
except Exception as exc :
805
- _log .exception ("Calling the after function failed." )
806
807
exc .__context__ = error
807
- traceback . print_exception ( type ( exc ), exc , exc . __traceback__ )
808
+ _log . exception ( "Calling the after function failed." , exc_info = exc )
808
809
elif error :
809
810
msg = f"Exception in voice thread { self .name } "
810
811
_log .exception (msg , exc_info = error )
811
- print (msg , file = sys .stderr )
812
- traceback .print_exception (type (error ), error , error .__traceback__ )
813
812
814
813
def stop (self ) -> None :
815
814
self ._end .set ()
816
815
self ._resumed .set ()
817
- self ._speak (False )
816
+ self ._speak (SpeakingState . none )
818
817
819
818
def pause (self , * , update_speaking : bool = True ) -> None :
820
819
self ._resumed .clear ()
821
820
if update_speaking :
822
- self ._speak (False )
821
+ self ._speak (SpeakingState . none )
823
822
824
823
def resume (self , * , update_speaking : bool = True ) -> None :
825
824
self ._played_frames_offset += self .loops
826
825
self .loops = 0
827
826
self ._start = time .perf_counter ()
828
827
self ._resumed .set ()
829
828
if update_speaking :
830
- self ._speak (True )
829
+ self ._speak (SpeakingState . voice )
831
830
832
831
def is_playing (self ) -> bool :
833
832
return self ._resumed .is_set () and not self ._end .is_set ()
@@ -841,14 +840,21 @@ def _set_source(self, source: AudioSource) -> None:
841
840
self .source = source
842
841
self .resume (update_speaking = False )
843
842
844
- def _speak (self , speaking : bool ) -> None :
843
+ def _speak (self , state : SpeakingState ) -> None :
845
844
try :
846
845
asyncio .run_coroutine_threadsafe (
847
- self .client .ws .speak (speaking ), self .client .loop
846
+ self .client .ws .speak (state ), self .client .loop
848
847
)
849
848
except Exception as e :
850
849
_log .info ("Speaking call in player failed: %s" , e )
851
850
852
851
def played_frames (self ) -> int :
853
852
"""Gets the number of 20ms frames played since the start of the audio file."""
854
853
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