Skip to content

Commit 6949773

Browse files
cursoragentangelo
andcommitted
Refactor audio interface to properly handle start/stop and threads
Co-authored-by: angelo <angelo@elevenlabs.io>
1 parent 0430688 commit 6949773

1 file changed

Lines changed: 24 additions & 12 deletions

File tree

src/elevenlabs/conversational_ai/default_audio_interface.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ def __init__(self):
1616
except ImportError:
1717
raise ImportError("To use DefaultAudioInterface you must install pyaudio.")
1818
self.pyaudio = pyaudio
19+
self.should_stop = threading.Event()
20+
self.output_thread = None
1921

2022
def start(self, input_callback: Callable[[bytes], None]):
2123
# Audio input is using callbacks from pyaudio which we simply pass through.
@@ -24,7 +26,7 @@ def start(self, input_callback: Callable[[bytes], None]):
2426
# Audio output is buffered so we can handle interruptions.
2527
# Start a separate thread to handle writing to the output stream.
2628
self.output_queue: queue.Queue[bytes] = queue.Queue()
27-
self.should_stop = threading.Event()
29+
self.should_stop.clear() # Reset the event in case start is called multiple times
2830
self.output_thread = threading.Thread(target=self._output_thread)
2931

3032
self.p = self.pyaudio.PyAudio()
@@ -50,11 +52,15 @@ def start(self, input_callback: Callable[[bytes], None]):
5052

5153
def stop(self):
5254
self.should_stop.set()
53-
self.output_thread.join()
54-
self.in_stream.stop_stream()
55-
self.in_stream.close()
56-
self.out_stream.close()
57-
self.p.terminate()
55+
if self.output_thread and self.output_thread.is_alive():
56+
self.output_thread.join()
57+
if hasattr(self, 'in_stream'):
58+
self.in_stream.stop_stream()
59+
self.in_stream.close()
60+
if hasattr(self, 'out_stream'):
61+
self.out_stream.close()
62+
if hasattr(self, 'p'):
63+
self.p.terminate()
5864

5965
def output(self, audio: bytes):
6066
self.output_queue.put(audio)
@@ -94,6 +100,8 @@ def __init__(self):
94100
except ImportError:
95101
raise ImportError("To use AsyncDefaultAudioInterface you must install pyaudio.")
96102
self.pyaudio = pyaudio
103+
self.should_stop = asyncio.Event()
104+
self.output_task = None
97105

98106
async def start(self, input_callback: Callable[[bytes], Awaitable[None]]):
99107
# Audio input is using callbacks from pyaudio which we adapt to async
@@ -102,7 +110,7 @@ async def start(self, input_callback: Callable[[bytes], Awaitable[None]]):
102110
# Audio output is buffered so we can handle interruptions.
103111
# Start a separate task to handle writing to the output stream.
104112
self.output_queue: asyncio.Queue[bytes] = asyncio.Queue()
105-
self.should_stop = asyncio.Event()
113+
self.should_stop.clear() # Reset the event in case start is called multiple times
106114

107115
self.p = self.pyaudio.PyAudio()
108116
self.in_stream = self.p.open(
@@ -128,11 +136,15 @@ async def start(self, input_callback: Callable[[bytes], Awaitable[None]]):
128136

129137
async def stop(self):
130138
self.should_stop.set()
131-
await self.output_task
132-
self.in_stream.stop_stream()
133-
self.in_stream.close()
134-
self.out_stream.close()
135-
self.p.terminate()
139+
if self.output_task and not self.output_task.done():
140+
await self.output_task
141+
if hasattr(self, 'in_stream'):
142+
self.in_stream.stop_stream()
143+
self.in_stream.close()
144+
if hasattr(self, 'out_stream'):
145+
self.out_stream.close()
146+
if hasattr(self, 'p'):
147+
self.p.terminate()
136148

137149
async def output(self, audio: bytes):
138150
await self.output_queue.put(audio)

0 commit comments

Comments
 (0)