18
18
class SoundrendererPygame (threading .Thread , SoundRenderer ):
19
19
"""Uses pygame.mixer to play sound"""
20
20
21
- def __init__ (self , audioformat , queue = None , pygame_buffersize = None ):
21
+ def __init__ (self , audioformat , queue = None , pygame_buffersize = None ,
22
+ pygame_channel_id = None ):
22
23
"""Constructor.
23
24
Creates a pygame sound renderer using pygame.mixer.
24
25
@@ -31,6 +32,9 @@ def __init__(self, audioformat, queue=None, pygame_buffersize=None):
31
32
audio frames are placed by the decoder (default=None).
32
33
pygame_buffersize : int, optional
33
34
The buffersize to be used in the Pygame mixer (default=None).
35
+ pygame_channel_id : int, optional
36
+ The ID of a specific pygame mixer channel to use. If none set,
37
+ an appropriate channel is selected automatically (default=None).
34
38
35
39
"""
36
40
global pygame
@@ -63,6 +67,8 @@ def __init__(self, audioformat, queue=None, pygame_buffersize=None):
63
67
else :
64
68
self ._own_mixer = False
65
69
70
+ self ._channel_id = pygame_channel_id
71
+
66
72
def run (self ):
67
73
"""Main thread function."""
68
74
global pygame
@@ -75,8 +81,15 @@ def run(self):
75
81
if not hasattr (self , "queue" ):
76
82
raise RuntimeError ("Audio queue is not intialized." )
77
83
84
+ if self ._channel_id is not None :
85
+ channel = pygame .mixer .Channel (self ._channel_id )
86
+ else :
87
+ channel = pygame .mixer .find_channel (force = True )
88
+
78
89
chunk = None
79
- channel = None
90
+ first_chunk_played = False
91
+ last_channel_state = None
92
+ last_channel_state_change = None
80
93
self .keep_listening = True
81
94
while self .keep_listening :
82
95
if chunk is None :
@@ -101,12 +114,27 @@ def run(self):
101
114
except Empty :
102
115
continue
103
116
104
- if channel is None :
105
- channel = chunk .play ()
117
+ if not first_chunk_played :
118
+ channel .play (chunk )
119
+ chunk = None
120
+ first_chunk_played = True
106
121
else :
107
- if not channel .get_queue ():
122
+ playing_sound = channel .get_sound ()
123
+ queued_sound = channel .get_queue ()
124
+ if not queued_sound :
108
125
channel .queue (chunk )
109
126
chunk = None
127
+ else :
128
+ # Fix Pygame channel getting "stuck" (for some reason)
129
+ now = time .perf_counter ()
130
+ if last_channel_state != [playing_sound , queued_sound ]:
131
+ last_channel_state = [playing_sound , queued_sound ]
132
+ last_channel_state_change = now
133
+ elif last_channel_state_change is not None :
134
+ if now - last_channel_state_change > \
135
+ 2 * chunk .get_length ():
136
+ channel .play (chunk )
137
+
110
138
time .sleep (0.005 )
111
139
112
140
if not channel is None and pygame .mixer .get_init ():
0 commit comments