|
1 | 1 | import daily |
2 | 2 | import threading |
3 | 3 | import pyaudio |
4 | | -import wave |
5 | | -from speexdsp import EchoCanceller |
6 | 4 |
|
7 | 5 | SAMPLE_RATE = 16000 |
8 | 6 | NUM_CHANNELS = 1 |
@@ -17,75 +15,11 @@ def is_playable_speaker(participant): |
17 | 15 | return is_speaker and is_subscribed and is_playable |
18 | 16 |
|
19 | 17 |
|
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 | | - |
82 | 18 | class DailyCall(daily.EventHandler): |
83 | 19 | def __init__(self): |
84 | 20 | daily.Daily.init() |
85 | 21 |
|
86 | 22 | self.__audio_interface = pyaudio.PyAudio() |
87 | | - self.__echo_canceller = EchoCancellerWrapper( |
88 | | - CHUNK_SIZE, 256, SAMPLE_RATE, self.__audio_interface) |
89 | 23 |
|
90 | 24 | self.__input_audio_stream = self.__audio_interface.open( |
91 | 25 | format=pyaudio.paInt16, |
@@ -146,14 +80,11 @@ def __init__(self): |
146 | 80 | self.__start_event = threading.Event() |
147 | 81 | self.__receive_bot_audio_thread = threading.Thread( |
148 | 82 | 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) |
153 | 85 |
|
154 | 86 | 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() |
157 | 88 |
|
158 | 89 | def on_inputs_updated(self, inputs): |
159 | 90 | self.__app_inputs_updated = True |
@@ -186,8 +117,7 @@ def join(self, meeting_url): |
186 | 117 | def leave(self): |
187 | 118 | self.__app_quit = True |
188 | 119 | 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() |
191 | 121 | self.__call_client.leave() |
192 | 122 |
|
193 | 123 | def maybe_start(self): |
@@ -197,33 +127,22 @@ def maybe_start(self): |
197 | 127 | if self.__app_inputs_updated and self.__app_joined: |
198 | 128 | self.__start_event.set() |
199 | 129 |
|
200 | | - def send_echo_cancelled_mic_audio(self): |
| 130 | + def send_user_audio(self): |
201 | 131 | self.__start_event.wait() |
202 | 132 |
|
203 | 133 | if self.__app_error: |
204 | | - print(f"Unable to send mic audio!") |
| 134 | + print(f"Unable to receive mic audio!") |
205 | 135 | return |
206 | 136 |
|
207 | 137 | 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) |
209 | 140 | if len(buffer) > 0: |
210 | 141 | try: |
211 | 142 | self.__mic_device.write_frames(buffer) |
212 | 143 | except Exception as e: |
213 | 144 | print(e) |
214 | 145 |
|
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 | | - |
227 | 146 | def receive_bot_audio(self): |
228 | 147 | self.__start_event.wait() |
229 | 148 |
|
@@ -233,7 +152,6 @@ def receive_bot_audio(self): |
233 | 152 |
|
234 | 153 | while not self.__app_quit: |
235 | 154 | buffer = self.__speaker_device.read_frames(CHUNK_SIZE) |
236 | | - self.__echo_canceller.send_speaker_audio(buffer) |
237 | 155 |
|
238 | 156 | if len(buffer) > 0: |
239 | 157 | self.__output_audio_stream.write(buffer, CHUNK_SIZE) |