Skip to content

Commit 78c207b

Browse files
[HOLD] Reintroduce TTS WS
update
1 parent abc610e commit 78c207b

File tree

31 files changed

+3136
-217
lines changed

31 files changed

+3136
-217
lines changed

deepgram/__init__.py

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,12 @@
103103
from .client import (
104104
SpeakOptions,
105105
SpeakRESTOptions,
106-
# SpeakWebSocketOptions,
106+
SpeakWSOptions,
107107
# FileSource,
108108
SpeakRestSource,
109109
SpeakSource,
110110
)
111-
from .client import SpeakWebSocketEvents
111+
from .client import SpeakWebSocketEvents, SpeakWebSocketMessage
112112

113113
## speak REST
114114
from .client import (
@@ -122,21 +122,23 @@
122122
SpeakRESTResponse,
123123
)
124124

125-
# ## speak WebSocket
126-
# from .client import (
127-
# SpeakWebSocketClient,
128-
# AsyncSpeakWebSocketClient,
129-
# )
130-
# from .client import (
131-
# SpeakWebSocketResponse,
132-
# # OpenResponse,
133-
# # MetadataResponse,
134-
# FlushedResponse,
135-
# # CloseResponse,
136-
# # UnhandledResponse,
137-
# WarningResponse,
138-
# # ErrorResponse,
139-
# )
125+
## speak WebSocket
126+
from .client import (
127+
SpeakWebSocketClient,
128+
AsyncSpeakWebSocketClient,
129+
SpeakWSClient,
130+
AsyncSpeakWSClient,
131+
)
132+
from .client import (
133+
# OpenResponse,
134+
# MetadataResponse,
135+
FlushedResponse,
136+
ClearedResponse,
137+
# CloseResponse,
138+
# UnhandledResponse,
139+
WarningResponse,
140+
# ErrorResponse,
141+
)
140142

141143
# manage
142144
from .client import ManageClient, AsyncManageClient
@@ -180,10 +182,26 @@
180182
)
181183

182184
# utilities
185+
# pylint: disable=wrong-import-position
183186
from .audio import Microphone, DeepgramMicrophoneError
184187
from .audio import (
185-
LOGGING,
186-
CHANNELS,
187-
RATE,
188-
CHUNK,
188+
INPUT_LOGGING,
189+
INPUT_CHANNELS,
190+
INPUT_RATE,
191+
INPUT_CHUNK,
189192
)
193+
194+
LOGGING = INPUT_LOGGING
195+
CHANNELS = INPUT_CHANNELS
196+
RATE = INPUT_RATE
197+
CHUNK = INPUT_CHUNK
198+
199+
from .audio import Speaker
200+
from .audio import (
201+
OUTPUT_LOGGING,
202+
OUTPUT_CHANNELS,
203+
OUTPUT_RATE,
204+
OUTPUT_CHUNK,
205+
)
206+
207+
# pylint: enable=wrong-import-position

deepgram/audio/__init__.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,19 @@
33
# SPDX-License-Identifier: MIT
44

55
from .microphone import Microphone
6-
from .microphone import LOGGING, CHANNELS, RATE, CHUNK
76
from .microphone import DeepgramMicrophoneError
7+
from .microphone import (
8+
LOGGING as INPUT_LOGGING,
9+
CHANNELS as INPUT_CHANNELS,
10+
RATE as INPUT_RATE,
11+
CHUNK as INPUT_CHUNK,
12+
)
13+
14+
from .speaker import Speaker
15+
from .speaker import DeepgramSpeakerError
16+
from .speaker import (
17+
LOGGING as OUTPUT_LOGGING,
18+
CHANNELS as OUTPUT_CHANNELS,
19+
RATE as OUTPUT_RATE,
20+
CHUNK as OUTPUT_CHUNK,
21+
)

deepgram/audio/microphone/constants.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from ...utils import verboselogs
66

77
# Constants for microphone
8-
98
LOGGING = verboselogs.WARNING
109
CHANNELS = 1
1110
RATE = 16000

deepgram/audio/microphone/microphone.py

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import inspect
66
import asyncio
77
import threading
8-
from typing import Optional, Callable, TYPE_CHECKING
8+
from typing import Optional, Callable, Union, TYPE_CHECKING
99
import logging
1010

1111
from ...utils import verboselogs
@@ -21,10 +21,10 @@ class Microphone: # pylint: disable=too-many-instance-attributes
2121
"""
2222

2323
_logger: verboselogs.VerboseLogger
24-
_exit: threading.Event
2524

2625
_audio: "pyaudio.PyAudio"
2726
_stream: "pyaudio.Stream"
27+
2828
_chunk: int
2929
_rate: int
3030
_format: int
@@ -34,9 +34,10 @@ class Microphone: # pylint: disable=too-many-instance-attributes
3434

3535
_asyncio_loop: asyncio.AbstractEventLoop
3636
_asyncio_thread: threading.Thread
37+
_exit: threading.Event
3738

38-
_push_callback_org: object
39-
_push_callback: object
39+
_push_callback_org: Optional[Callable] = None
40+
_push_callback: Optional[Callable] = None
4041

4142
def __init__(
4243
self,
@@ -53,6 +54,7 @@ def __init__(
5354
self._logger = verboselogs.VerboseLogger(__name__)
5455
self._logger.addHandler(logging.StreamHandler())
5556
self._logger.setLevel(verbose)
57+
5658
self._exit = threading.Event()
5759

5860
self._audio = pyaudio.PyAudio()
@@ -71,9 +73,16 @@ def _start_asyncio_loop(self) -> None:
7173

7274
def is_active(self) -> bool:
7375
"""
74-
returns True if the stream is active, False otherwise
76+
is_active - returns the state of the stream
77+
78+
Args:
79+
None
80+
81+
Returns:
82+
True if the stream is active, False otherwise
7583
"""
7684
self._logger.debug("Microphone.is_active ENTER")
85+
7786
if self._stream is None:
7887
self._logger.error("stream is None")
7988
self._logger.debug("Microphone.is_active LEAVE")
@@ -87,24 +96,34 @@ def is_active(self) -> bool:
8796

8897
def set_callback(self, push_callback: Callable) -> None:
8998
"""
90-
Set the callback function to be called when data is received.
99+
set_callback - sets the callback function to be called when data is received.
100+
101+
Args:
102+
push_callback (Callable): The callback function to be called when data is received.
103+
This should be the websocket send function.
104+
105+
Returns:
106+
None
91107
"""
92108
self._push_callback_org = push_callback
93109

94110
def start(self) -> bool:
95111
"""
96-
starts the microphone stream
112+
starts - starts the microphone stream
113+
114+
Returns:
115+
bool: True if the stream was started, False otherwise
97116
"""
98117
self._logger.debug("Microphone.start ENTER")
99118

100119
self._logger.info("format: %s", self._format)
101120
self._logger.info("channels: %d", self._channels)
102121
self._logger.info("rate: %d", self._rate)
103122
self._logger.info("chunk: %d", self._chunk)
104-
self._logger.info("input_device_id: %d", self._input_device_index)
123+
# self._logger.info("input_device_id: %d", self._input_device_index)
105124

106125
if self._push_callback_org is None:
107-
self._logger.error("start() failed. No callback set.")
126+
self._logger.error("start failed. No callback set.")
108127
self._logger.debug("Microphone.start LEAVE")
109128
return False
110129

@@ -114,9 +133,13 @@ def start(self) -> bool:
114133
self._asyncio_thread = threading.Thread(target=self._start_asyncio_loop)
115134
self._asyncio_thread.start()
116135

117-
self._push_callback = lambda data: asyncio.run_coroutine_threadsafe(
118-
self._push_callback_org(data), self._asyncio_loop
119-
).result()
136+
self._push_callback = lambda data: (
137+
asyncio.run_coroutine_threadsafe(
138+
self._push_callback_org(data), self._asyncio_loop
139+
).result()
140+
if self._push_callback_org
141+
else None
142+
)
120143
else:
121144
self._logger.verbose("regular threaded callback")
122145
self._push_callback = self._push_callback_org
@@ -134,7 +157,7 @@ def start(self) -> bool:
134157
self._exit.clear()
135158
self._stream.start_stream()
136159

137-
self._logger.notice("start() succeeded")
160+
self._logger.notice("start succeeded")
138161
self._logger.debug("Microphone.start LEAVE")
139162
return True
140163

@@ -176,41 +199,50 @@ def _callback(
176199

177200
def mute(self) -> bool:
178201
"""
179-
Mutes the microphone stream
202+
mute - mutes the microphone stream
203+
204+
Returns:
205+
bool: True if the stream was muted, False otherwise
180206
"""
181207
self._logger.debug("Microphone.mute ENTER")
182208

183209
if self._stream is None:
184-
self._logger.error("mute() failed. Library not initialized.")
210+
self._logger.error("mute failed. Library not initialized.")
185211
self._logger.debug("Microphone.mute LEAVE")
186212
return False
187213

188214
self._is_muted = True
189215

190-
self._logger.notice("mute() succeeded")
216+
self._logger.notice("mute succeeded")
191217
self._logger.debug("Microphone.mute LEAVE")
192218
return True
193219

194220
def unmute(self) -> bool:
195221
"""
196-
Unmutes the microphone stream
222+
unmute - unmutes the microphone stream
223+
224+
Returns:
225+
bool: True if the stream was unmuted, False otherwise
197226
"""
198227
self._logger.debug("Microphone.unmute ENTER")
199228

200229
if self._stream is None:
201-
self._logger.error("unmute() failed. Library not initialized.")
230+
self._logger.error("unmute failed. Library not initialized.")
202231
self._logger.debug("Microphone.unmute LEAVE")
203232
return False
204233

205234
self._is_muted = False
206235

207-
self._logger.notice("unmute() succeeded")
236+
self._logger.notice("unmute succeeded")
208237
self._logger.debug("Microphone.unmute LEAVE")
209238
return True
210239

211240
def finish(self) -> bool:
212241
"""
213-
Stops the microphone stream
242+
finish - stops the microphone stream
243+
244+
Returns:
245+
bool: True if the stream was stopped, False otherwise
214246
"""
215247
self._logger.debug("Microphone.finish ENTER")
216248

@@ -219,19 +251,24 @@ def finish(self) -> bool:
219251

220252
# Stop the stream.
221253
if self._stream is not None:
254+
self._logger.notice("stopping stream...")
222255
self._stream.stop_stream()
223256
self._stream.close()
224257
self._stream = None # type: ignore
258+
self._logger.notice("stream stopped")
225259

226260
# clean up the thread
227261
if (
228-
inspect.iscoroutinefunction(self._push_callback_org)
229-
and self._asyncio_thread is not None
262+
# inspect.iscoroutinefunction(self._push_callback_org)
263+
# and
264+
self._asyncio_thread
265+
is not None
230266
):
267+
self._logger.notice("stopping asyncio loop...")
231268
self._asyncio_loop.call_soon_threadsafe(self._asyncio_loop.stop)
232269
self._asyncio_thread.join()
233270
self._asyncio_thread = None # type: ignore
234-
self._logger.notice("stream/recv thread joined")
271+
self._logger.notice("_asyncio_thread joined")
235272

236273
self._logger.notice("finish succeeded")
237274
self._logger.debug("Microphone.finish LEAVE")

deepgram/audio/speaker/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright 2023-2024 Deepgram SDK contributors. All Rights Reserved.
2+
# Use of this source code is governed by a MIT license that can be found in the LICENSE file.
3+
# SPDX-License-Identifier: MIT
4+
5+
from .speaker import Speaker
6+
from .errors import DeepgramSpeakerError
7+
from .constants import LOGGING, CHANNELS, RATE, CHUNK

deepgram/audio/speaker/constants.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright 2024 Deepgram SDK contributors. All Rights Reserved.
2+
# Use of this source code is governed by a MIT license that can be found in the LICENSE file.
3+
# SPDX-License-Identifier: MIT
4+
5+
from ...utils import verboselogs
6+
7+
# Constants for microphone
8+
LOGGING = verboselogs.WARNING
9+
TIMEOUT = 0.050
10+
CHANNELS = 1
11+
RATE = 16000
12+
CHUNK = 8194

deepgram/audio/speaker/errors.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Copyright 2024 Deepgram SDK contributors. All Rights Reserved.
2+
# Use of this source code is governed by a MIT license that can be found in the LICENSE file.
3+
# SPDX-License-Identifier: MIT
4+
5+
6+
# exceptions for speaker
7+
class DeepgramSpeakerError(Exception):
8+
"""
9+
Exception raised for known errors related to Speaker library.
10+
11+
Attributes:
12+
message (str): The error message describing the exception.
13+
"""
14+
15+
def __init__(self, message: str):
16+
super().__init__(message)
17+
self.name = "DeepgramSpeakerError"
18+
self.message = message
19+
20+
def __str__(self):
21+
return f"{self.name}: {self.message}"

0 commit comments

Comments
 (0)