11import daily
22import threading
33import pyaudio
4- import wave
5- from speexdsp import EchoCanceller
64
75SAMPLE_RATE = 16000
86NUM_CHANNELS = 1
@@ -17,75 +15,11 @@ def is_playable_speaker(participant):
1715 return is_speaker and is_subscribed and is_playable
1816
1917
20- class EchoCancellerWrapper ():
21- def __init__ (self , chunk_size , filter_length , sample_rate , audio_interface ):
22- self .__audio_interface = audio_interface
23- self .__echo_canceller = EchoCanceller .create (
24- int (chunk_size / 2 ), filter_length , sample_rate )
25- self .__mic_buffer = b''
26- self .__speaker_buffer = b''
27-
28- # Initialize wav files for mic input, speaker input, and processed output
29- self .__mic_wav = wave .open ('mic_input.wav' , 'wb' )
30- self .__mic_wav .setnchannels (NUM_CHANNELS )
31- self .__mic_wav .setsampwidth (
32- self .__audio_interface .get_sample_size (pyaudio .paInt16 ))
33- self .__mic_wav .setframerate (SAMPLE_RATE )
34-
35- self .__speaker_wav = wave .open ('speaker_input.wav' , 'wb' )
36- self .__speaker_wav .setnchannels (NUM_CHANNELS )
37- self .__speaker_wav .setsampwidth (
38- self .__audio_interface .get_sample_size (pyaudio .paInt16 ))
39- self .__speaker_wav .setframerate (SAMPLE_RATE )
40-
41- self .__output_wav = wave .open ('processed_output.wav' , 'wb' )
42- self .__output_wav .setnchannels (NUM_CHANNELS )
43- self .__output_wav .setsampwidth (
44- self .__audio_interface .get_sample_size (pyaudio .paInt16 ))
45- self .__output_wav .setframerate (SAMPLE_RATE )
46-
47- def send_mic_audio (self , audio ):
48- self .__mic_buffer += audio
49-
50- def send_speaker_audio (self , audio ):
51- self .__speaker_buffer += audio
52-
53- def read (self , length ):
54- min_length = min (len (self .__mic_buffer ), length )
55- if min_length > 0 :
56- mic_audio = self .__mic_buffer [:min_length ]
57- speaker_audio = self .__speaker_buffer [:min_length ]
58- # If speaker buffer is less than mic buffer, pad it with silence
59- if len (speaker_audio ) < len (mic_audio ):
60- speaker_audio += b'\x00 ' * \
61- (len (mic_audio ) - len (speaker_audio ))
62-
63- self .__mic_wav .writeframes (mic_audio )
64- self .__speaker_wav .writeframes (speaker_audio )
65-
66- self .__mic_buffer = self .__mic_buffer [min_length :]
67- self .__speaker_buffer = self .__speaker_buffer [min_length :]
68- processed_audio = self .__echo_canceller .process (
69- mic_audio , speaker_audio )
70- # Write processed output to wav file
71- self .__output_wav .writeframes (processed_audio )
72- return processed_audio
73- return b''
74-
75- def __del__ (self ):
76- # Close wav files when the object is destroyed
77- self .__mic_wav .close ()
78- self .__speaker_wav .close ()
79- self .__output_wav .close ()
80-
81-
8218class DailyCall (daily .EventHandler ):
8319 def __init__ (self ):
8420 daily .Daily .init ()
8521
8622 self .__audio_interface = pyaudio .PyAudio ()
87- self .__echo_canceller = EchoCancellerWrapper (
88- CHUNK_SIZE , 256 , SAMPLE_RATE , self .__audio_interface )
8923
9024 self .__input_audio_stream = self .__audio_interface .open (
9125 format = pyaudio .paInt16 ,
@@ -146,14 +80,11 @@ def __init__(self):
14680 self .__start_event = threading .Event ()
14781 self .__receive_bot_audio_thread = threading .Thread (
14882 target = self .receive_bot_audio )
149- self .__receive_mic_audio_thread = threading .Thread (
150- target = self .receive_mic_audio )
151- self .__send_echo_cancelled_mic_audio_thread = threading .Thread (
152- target = self .send_echo_cancelled_mic_audio )
83+ self .__send_user_audio_thread = threading .Thread (
84+ target = self .send_user_audio )
15385
15486 self .__receive_bot_audio_thread .start ()
155- self .__receive_mic_audio_thread .start ()
156- self .__send_echo_cancelled_mic_audio_thread .start ()
87+ self .__send_user_audio_thread .start ()
15788
15889 def on_inputs_updated (self , inputs ):
15990 self .__app_inputs_updated = True
@@ -186,8 +117,7 @@ def join(self, meeting_url):
186117 def leave (self ):
187118 self .__app_quit = True
188119 self .__receive_bot_audio_thread .join ()
189- self .__receive_mic_audio_thread .join ()
190- self .__send_echo_cancelled_mic_audio_thread .join ()
120+ self .__send_user_audio_thread .join ()
191121 self .__call_client .leave ()
192122
193123 def maybe_start (self ):
@@ -197,33 +127,22 @@ def maybe_start(self):
197127 if self .__app_inputs_updated and self .__app_joined :
198128 self .__start_event .set ()
199129
200- def send_echo_cancelled_mic_audio (self ):
130+ def send_user_audio (self ):
201131 self .__start_event .wait ()
202132
203133 if self .__app_error :
204- print (f"Unable to send mic audio!" )
134+ print (f"Unable to receive mic audio!" )
205135 return
206136
207137 while not self .__app_quit :
208- buffer = self .__echo_canceller .read (CHUNK_SIZE )
138+ buffer = self .__input_audio_stream .read (
139+ CHUNK_SIZE , exception_on_overflow = False )
209140 if len (buffer ) > 0 :
210141 try :
211142 self .__mic_device .write_frames (buffer )
212143 except Exception as e :
213144 print (e )
214145
215- def receive_mic_audio (self ):
216- self .__start_event .wait ()
217-
218- if self .__app_error :
219- print (f"Unable to receive mic audio!" )
220- return
221-
222- while not self .__app_quit :
223- buffer = self .__input_audio_stream .read (
224- CHUNK_SIZE , exception_on_overflow = False )
225- self .__echo_canceller .send_mic_audio (buffer )
226-
227146 def receive_bot_audio (self ):
228147 self .__start_event .wait ()
229148
@@ -233,7 +152,6 @@ def receive_bot_audio(self):
233152
234153 while not self .__app_quit :
235154 buffer = self .__speaker_device .read_frames (CHUNK_SIZE )
236- self .__echo_canceller .send_speaker_audio (buffer )
237155
238156 if len (buffer ) > 0 :
239157 self .__output_audio_stream .write (buffer , CHUNK_SIZE )
0 commit comments