Merge pull request #658 from pipecat-ai/aleix/default-to-24000-sample-rate
update TTS and transport output sample rate to 24000
This commit is contained in:
@@ -13,6 +13,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
grained control of what media subscriptions you want for each participant in a
|
grained control of what media subscriptions you want for each participant in a
|
||||||
room.
|
room.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Changed default output sample rate to 24000. This changes all TTS service to
|
||||||
|
output to 24000 and also the default output transport sample rate. This
|
||||||
|
improves audio quality at the cost of some extra bandwidth.
|
||||||
|
|
||||||
## [0.0.47] - 2024-10-22
|
## [0.0.47] - 2024-10-22
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ async def main():
|
|||||||
url=url,
|
url=url,
|
||||||
token=token,
|
token=token,
|
||||||
room_name=room_name,
|
room_name=room_name,
|
||||||
params=LiveKitParams(audio_out_enabled=True, audio_out_sample_rate=16000),
|
params=LiveKitParams(audio_out_enabled=True),
|
||||||
)
|
)
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
tts = CartesiaTTSService(
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ async def main():
|
|||||||
"Respond bot",
|
"Respond bot",
|
||||||
DailyParams(
|
DailyParams(
|
||||||
audio_out_enabled=True,
|
audio_out_enabled=True,
|
||||||
audio_out_sample_rate=16000,
|
|
||||||
transcription_enabled=True,
|
transcription_enabled=True,
|
||||||
vad_enabled=True,
|
vad_enabled=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ async def main():
|
|||||||
"Respond bot",
|
"Respond bot",
|
||||||
DailyParams(
|
DailyParams(
|
||||||
audio_out_enabled=True,
|
audio_out_enabled=True,
|
||||||
audio_out_sample_rate=16000,
|
|
||||||
vad_enabled=True,
|
vad_enabled=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_audio_passthrough=True,
|
vad_audio_passthrough=True,
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ async def main():
|
|||||||
"Test",
|
"Test",
|
||||||
DailyParams(
|
DailyParams(
|
||||||
audio_in_enabled=True,
|
audio_in_enabled=True,
|
||||||
|
audio_in_sample_rate=24000,
|
||||||
audio_out_enabled=True,
|
audio_out_enabled=True,
|
||||||
camera_out_enabled=True,
|
camera_out_enabled=True,
|
||||||
camera_out_is_live=True,
|
camera_out_is_live=True,
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ async def main():
|
|||||||
tk_root.title("Local Mirror")
|
tk_root.title("Local Mirror")
|
||||||
|
|
||||||
daily_transport = DailyTransport(
|
daily_transport = DailyTransport(
|
||||||
room_url, token, "Test", DailyParams(audio_in_enabled=True)
|
room_url, token, "Test", DailyParams(audio_in_enabled=True, audio_in_sample_rate=24000)
|
||||||
)
|
)
|
||||||
|
|
||||||
tk_transport = TkLocalTransport(
|
tk_transport = TkLocalTransport(
|
||||||
|
|||||||
@@ -78,9 +78,6 @@ async def main():
|
|||||||
tts = CartesiaTTSService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
voice_id="79a125e8-cd45-4c13-8a67-188112f4dd22", # British Lady
|
voice_id="79a125e8-cd45-4c13-8a67-188112f4dd22", # British Lady
|
||||||
params=CartesiaTTSService.InputParams(
|
|
||||||
sample_rate=16000,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_first_participant_joined")
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import LLMMessagesFrame
|
from pipecat.frames.frames import LLMMessagesFrame, TTSUpdateSettingsFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.parallel_pipeline import ParallelPipeline
|
from pipecat.pipeline.parallel_pipeline import ParallelPipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
@@ -19,7 +19,6 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|||||||
from pipecat.processors.filters.function_filter import FunctionFilter
|
from pipecat.processors.filters.function_filter import FunctionFilter
|
||||||
from pipecat.services.cartesia import CartesiaTTSService
|
from pipecat.services.cartesia import CartesiaTTSService
|
||||||
from pipecat.services.openai import OpenAILLMService
|
from pipecat.services.openai import OpenAILLMService
|
||||||
from pipecat.services.whisper import Model, WhisperSTTService
|
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
||||||
|
|
||||||
from openai.types.chat import ChatCompletionToolParam
|
from openai.types.chat import ChatCompletionToolParam
|
||||||
@@ -61,16 +60,14 @@ async def main():
|
|||||||
token,
|
token,
|
||||||
"Pipecat",
|
"Pipecat",
|
||||||
DailyParams(
|
DailyParams(
|
||||||
audio_in_enabled=True,
|
|
||||||
audio_out_enabled=True,
|
audio_out_enabled=True,
|
||||||
|
transcription_enabled=True,
|
||||||
vad_enabled=True,
|
vad_enabled=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_audio_passthrough=True,
|
vad_audio_passthrough=True,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
stt = WhisperSTTService(model=Model.LARGE)
|
|
||||||
|
|
||||||
english_tts = CartesiaTTSService(
|
english_tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
voice_id="79a125e8-cd45-4c13-8a67-188112f4dd22", # British Lady
|
voice_id="79a125e8-cd45-4c13-8a67-188112f4dd22", # British Lady
|
||||||
@@ -116,7 +113,6 @@ async def main():
|
|||||||
pipeline = Pipeline(
|
pipeline = Pipeline(
|
||||||
[
|
[
|
||||||
transport.input(), # Transport user input
|
transport.input(), # Transport user input
|
||||||
stt, # STT
|
|
||||||
context_aggregator.user(), # User responses
|
context_aggregator.user(), # User responses
|
||||||
llm, # LLM
|
llm, # LLM
|
||||||
ParallelPipeline( # TTS (bot will speak the chosen language)
|
ParallelPipeline( # TTS (bot will speak the chosen language)
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ from scipy import signal
|
|||||||
|
|
||||||
|
|
||||||
def resample_audio(audio: bytes, original_rate: int, target_rate: int) -> bytes:
|
def resample_audio(audio: bytes, original_rate: int, target_rate: int) -> bytes:
|
||||||
|
if original_rate == target_rate:
|
||||||
|
return audio
|
||||||
audio_data = np.frombuffer(audio, dtype=np.int16)
|
audio_data = np.frombuffer(audio, dtype=np.int16)
|
||||||
num_samples = int(len(audio) * target_rate / original_rate)
|
num_samples = int(len(audio) * target_rate / original_rate)
|
||||||
resampled_audio = signal.resample(audio_data, num_samples)
|
resampled_audio = signal.resample(audio_data, num_samples)
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class SileroOnnxModel:
|
|||||||
|
|
||||||
if sr not in self.sample_rates:
|
if sr not in self.sample_rates:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Supported sampling rates: {self.sample_rates} (or multiply of 16000)"
|
f"Supported sampling rates: {self.sample_rates} (or multiple of 16000)"
|
||||||
)
|
)
|
||||||
if sr / np.shape(x)[1] > 31.25:
|
if sr / np.shape(x)[1] > 31.25:
|
||||||
raise ValueError("Input audio chunk is too short")
|
raise ValueError("Input audio chunk is too short")
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ class TTSService(AIService):
|
|||||||
# if push_stop_frames is True, wait for this idle period before pushing TTSStoppedFrame
|
# if push_stop_frames is True, wait for this idle period before pushing TTSStoppedFrame
|
||||||
stop_frame_timeout_s: float = 1.0,
|
stop_frame_timeout_s: float = 1.0,
|
||||||
# TTS output sample rate
|
# TTS output sample rate
|
||||||
sample_rate: int = 16000,
|
sample_rate: int = 24000,
|
||||||
text_filter: Optional[BaseTextFilter] = None,
|
text_filter: Optional[BaseTextFilter] = None,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
@@ -514,7 +514,7 @@ class SegmentedSTTService(STTService):
|
|||||||
min_volume: float = 0.6,
|
min_volume: float = 0.6,
|
||||||
max_silence_secs: float = 0.3,
|
max_silence_secs: float = 0.3,
|
||||||
max_buffer_secs: float = 1.5,
|
max_buffer_secs: float = 1.5,
|
||||||
sample_rate: int = 16000,
|
sample_rate: int = 24000,
|
||||||
num_channels: int = 1,
|
num_channels: int = 1,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -25,8 +25,14 @@ from pipecat.frames.frames import (
|
|||||||
TTSStoppedFrame,
|
TTSStoppedFrame,
|
||||||
URLImageRawFrame,
|
URLImageRawFrame,
|
||||||
)
|
)
|
||||||
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.ai_services import ImageGenService, STTService, TTSService
|
from pipecat.services.ai_services import ImageGenService, STTService, TTSService
|
||||||
from pipecat.services.openai import BaseOpenAILLMService
|
from pipecat.services.openai import (
|
||||||
|
BaseOpenAILLMService,
|
||||||
|
OpenAIAssistantContextAggregator,
|
||||||
|
OpenAIContextAggregatorPair,
|
||||||
|
OpenAIUserContextAggregator,
|
||||||
|
)
|
||||||
from pipecat.transcriptions.language import Language
|
from pipecat.transcriptions.language import Language
|
||||||
from pipecat.utils.time import time_now_iso8601
|
from pipecat.utils.time import time_now_iso8601
|
||||||
|
|
||||||
@@ -38,6 +44,7 @@ try:
|
|||||||
SpeechConfig,
|
SpeechConfig,
|
||||||
SpeechRecognizer,
|
SpeechRecognizer,
|
||||||
SpeechSynthesizer,
|
SpeechSynthesizer,
|
||||||
|
SpeechSynthesisOutputFormat,
|
||||||
)
|
)
|
||||||
from azure.cognitiveservices.speech.audio import (
|
from azure.cognitiveservices.speech.audio import (
|
||||||
AudioStreamFormat,
|
AudioStreamFormat,
|
||||||
@@ -70,6 +77,33 @@ class AzureLLMService(BaseOpenAILLMService):
|
|||||||
api_version=self._api_version,
|
api_version=self._api_version,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_context_aggregator(
|
||||||
|
context: OpenAILLMContext, *, assistant_expect_stripped_words: bool = True
|
||||||
|
) -> OpenAIContextAggregatorPair:
|
||||||
|
user = OpenAIUserContextAggregator(context)
|
||||||
|
assistant = OpenAIAssistantContextAggregator(
|
||||||
|
user, expect_stripped_words=assistant_expect_stripped_words
|
||||||
|
)
|
||||||
|
return OpenAIContextAggregatorPair(_user=user, _assistant=assistant)
|
||||||
|
|
||||||
|
|
||||||
|
def sample_rate_to_output_format(sample_rate: int) -> SpeechSynthesisOutputFormat:
|
||||||
|
match sample_rate:
|
||||||
|
case 8000:
|
||||||
|
return SpeechSynthesisOutputFormat.Raw8Khz16BitMonoPcm
|
||||||
|
case 16000:
|
||||||
|
return SpeechSynthesisOutputFormat.Raw16Khz16BitMonoPcm
|
||||||
|
case 22050:
|
||||||
|
return SpeechSynthesisOutputFormat.Raw22050Hz16BitMonoPcm
|
||||||
|
case 24000:
|
||||||
|
return SpeechSynthesisOutputFormat.Raw24Khz16BitMonoPcm
|
||||||
|
case 44100:
|
||||||
|
return SpeechSynthesisOutputFormat.Raw44100Hz16BitMonoPcm
|
||||||
|
case 48000:
|
||||||
|
return SpeechSynthesisOutputFormat.Raw48Khz16BitMonoPcm
|
||||||
|
return SpeechSynthesisOutputFormat.Raw16Khz16BitMonoPcm
|
||||||
|
|
||||||
|
|
||||||
class AzureTTSService(TTSService):
|
class AzureTTSService(TTSService):
|
||||||
class InputParams(BaseModel):
|
class InputParams(BaseModel):
|
||||||
@@ -88,13 +122,15 @@ class AzureTTSService(TTSService):
|
|||||||
api_key: str,
|
api_key: str,
|
||||||
region: str,
|
region: str,
|
||||||
voice="en-US-SaraNeural",
|
voice="en-US-SaraNeural",
|
||||||
sample_rate: int = 16000,
|
sample_rate: int = 24000,
|
||||||
params: InputParams = InputParams(),
|
params: InputParams = InputParams(),
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
super().__init__(sample_rate=sample_rate, **kwargs)
|
super().__init__(sample_rate=sample_rate, **kwargs)
|
||||||
|
|
||||||
speech_config = SpeechConfig(subscription=api_key, region=region)
|
speech_config = SpeechConfig(subscription=api_key, region=region)
|
||||||
|
speech_config.set_speech_synthesis_output_format(sample_rate_to_output_format(sample_rate))
|
||||||
|
|
||||||
self._speech_synthesizer = SpeechSynthesizer(speech_config=speech_config, audio_config=None)
|
self._speech_synthesizer = SpeechSynthesizer(speech_config=speech_config, audio_config=None)
|
||||||
|
|
||||||
self._settings = {
|
self._settings = {
|
||||||
@@ -283,7 +319,7 @@ class AzureSTTService(STTService):
|
|||||||
api_key: str,
|
api_key: str,
|
||||||
region: str,
|
region: str,
|
||||||
language=Language.EN_US,
|
language=Language.EN_US,
|
||||||
sample_rate=16000,
|
sample_rate=24000,
|
||||||
channels=1,
|
channels=1,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ class CartesiaTTSService(WordTTSService):
|
|||||||
cartesia_version: str = "2024-06-10",
|
cartesia_version: str = "2024-06-10",
|
||||||
url: str = "wss://api.cartesia.ai/tts/websocket",
|
url: str = "wss://api.cartesia.ai/tts/websocket",
|
||||||
model: str = "sonic-english",
|
model: str = "sonic-english",
|
||||||
sample_rate: int = 16000,
|
sample_rate: int = 24000,
|
||||||
encoding: str = "pcm_s16le",
|
encoding: str = "pcm_s16le",
|
||||||
container: str = "raw",
|
container: str = "raw",
|
||||||
params: InputParams = InputParams(),
|
params: InputParams = InputParams(),
|
||||||
@@ -99,6 +99,7 @@ class CartesiaTTSService(WordTTSService):
|
|||||||
super().__init__(
|
super().__init__(
|
||||||
aggregate_sentences=True,
|
aggregate_sentences=True,
|
||||||
push_text_frames=False,
|
push_text_frames=False,
|
||||||
|
sample_rate=sample_rate,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -298,13 +299,13 @@ class CartesiaHttpTTSService(TTSService):
|
|||||||
voice_id: str,
|
voice_id: str,
|
||||||
model: str = "sonic-english",
|
model: str = "sonic-english",
|
||||||
base_url: str = "https://api.cartesia.ai",
|
base_url: str = "https://api.cartesia.ai",
|
||||||
sample_rate: int = 16000,
|
sample_rate: int = 24000,
|
||||||
encoding: str = "pcm_s16le",
|
encoding: str = "pcm_s16le",
|
||||||
container: str = "raw",
|
container: str = "raw",
|
||||||
params: InputParams = InputParams(),
|
params: InputParams = InputParams(),
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
super().__init__(**kwargs)
|
super().__init__(sample_rate=sample_rate, **kwargs)
|
||||||
|
|
||||||
self._api_key = api_key
|
self._api_key = api_key
|
||||||
self._settings = {
|
self._settings = {
|
||||||
|
|||||||
@@ -51,11 +51,11 @@ class DeepgramTTSService(TTSService):
|
|||||||
*,
|
*,
|
||||||
api_key: str,
|
api_key: str,
|
||||||
voice: str = "aura-helios-en",
|
voice: str = "aura-helios-en",
|
||||||
sample_rate: int = 16000,
|
sample_rate: int = 24000,
|
||||||
encoding: str = "linear16",
|
encoding: str = "linear16",
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
super().__init__(**kwargs)
|
super().__init__(sample_rate=sample_rate, **kwargs)
|
||||||
|
|
||||||
self._settings = {
|
self._settings = {
|
||||||
"sample_rate": sample_rate,
|
"sample_rate": sample_rate,
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class ElevenLabsTTSService(WordTTSService):
|
|||||||
voice_id: str,
|
voice_id: str,
|
||||||
model: str = "eleven_turbo_v2_5",
|
model: str = "eleven_turbo_v2_5",
|
||||||
url: str = "wss://api.elevenlabs.io",
|
url: str = "wss://api.elevenlabs.io",
|
||||||
output_format: ElevenLabsOutputFormat = "pcm_16000",
|
output_format: ElevenLabsOutputFormat = "pcm_24000",
|
||||||
params: InputParams = InputParams(),
|
params: InputParams = InputParams(),
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ class PlayHTTTSService(TTSService):
|
|||||||
user_id: str,
|
user_id: str,
|
||||||
voice_url: str,
|
voice_url: str,
|
||||||
voice_engine: str = "PlayHT3.0-mini",
|
voice_engine: str = "PlayHT3.0-mini",
|
||||||
sample_rate: int = 16000,
|
sample_rate: int = 24000,
|
||||||
output_format: str = "wav",
|
output_format: str = "wav",
|
||||||
params: InputParams = InputParams(),
|
params: InputParams = InputParams(),
|
||||||
**kwargs,
|
**kwargs,
|
||||||
@@ -310,7 +310,7 @@ class PlayHTHttpTTSService(TTSService):
|
|||||||
user_id: str,
|
user_id: str,
|
||||||
voice_url: str,
|
voice_url: str,
|
||||||
voice_engine: str = "PlayHT3.0-mini",
|
voice_engine: str = "PlayHT3.0-mini",
|
||||||
sample_rate: int = 16000,
|
sample_rate: int = 24000,
|
||||||
params: InputParams = InputParams(),
|
params: InputParams = InputParams(),
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -39,9 +39,10 @@ class XTTSService(TTSService):
|
|||||||
language: Language,
|
language: Language,
|
||||||
base_url: str,
|
base_url: str,
|
||||||
aiohttp_session: aiohttp.ClientSession,
|
aiohttp_session: aiohttp.ClientSession,
|
||||||
|
sample_rate: int = 24000,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
super().__init__(**kwargs)
|
super().__init__(sample_rate=sample_rate, **kwargs)
|
||||||
|
|
||||||
self._settings = {
|
self._settings = {
|
||||||
"language": self.language_to_service_language(language),
|
"language": self.language_to_service_language(language),
|
||||||
@@ -162,16 +163,18 @@ class XTTSService(TTSService):
|
|||||||
# Remove processed data from buffer
|
# Remove processed data from buffer
|
||||||
buffer = buffer[48000:]
|
buffer = buffer[48000:]
|
||||||
|
|
||||||
# Resample the audio from 24000 Hz to 16000 Hz
|
# Resample the audio from 24000 Hz
|
||||||
resampled_audio = resample_audio(bytes(process_data), 24000, 16000)
|
resampled_audio = resample_audio(
|
||||||
|
bytes(process_data), 24000, self._sample_rate
|
||||||
|
)
|
||||||
# Create the frame with the resampled audio
|
# Create the frame with the resampled audio
|
||||||
frame = TTSAudioRawFrame(resampled_audio, 16000, 1)
|
frame = TTSAudioRawFrame(resampled_audio, self._sample_rate, 1)
|
||||||
yield frame
|
yield frame
|
||||||
|
|
||||||
# Process any remaining data in the buffer
|
# Process any remaining data in the buffer
|
||||||
if len(buffer) > 0:
|
if len(buffer) > 0:
|
||||||
resampled_audio = resample_audio(bytes(buffer), 24000, 16000)
|
resampled_audio = resample_audio(bytes(buffer), 24000, self._sample_rate)
|
||||||
frame = TTSAudioRawFrame(resampled_audio, 16000, 1)
|
frame = TTSAudioRawFrame(resampled_audio, self._sample_rate, 1)
|
||||||
yield frame
|
yield frame
|
||||||
|
|
||||||
yield TTSStoppedFrame()
|
yield TTSStoppedFrame()
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class TransportParams(BaseModel):
|
|||||||
camera_out_color_format: str = "RGB"
|
camera_out_color_format: str = "RGB"
|
||||||
audio_out_enabled: bool = False
|
audio_out_enabled: bool = False
|
||||||
audio_out_is_live: bool = False
|
audio_out_is_live: bool = False
|
||||||
audio_out_sample_rate: int = 16000
|
audio_out_sample_rate: int = 24000
|
||||||
audio_out_channels: int = 1
|
audio_out_channels: int = 1
|
||||||
audio_out_bitrate: int = 96000
|
audio_out_bitrate: int = 96000
|
||||||
audio_in_enabled: bool = False
|
audio_in_enabled: bool = False
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from typing import Any, Awaitable, Callable, List
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from pipecat.audio.utils import resample_audio
|
from pipecat.audio.utils import resample_audio
|
||||||
|
from pipecat.audio.vad.vad_analyzer import VADAnalyzer
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
AudioRawFrame,
|
AudioRawFrame,
|
||||||
CancelFrame,
|
CancelFrame,
|
||||||
|
|||||||
Reference in New Issue
Block a user