fix : conditional stt & tts activation

This commit is contained in:
martin legrand
2025-06-03 02:19:50 +02:00
parent 81760bfd9c
commit a58b1cf9f8
2 changed files with 49 additions and 11 deletions
+38 -6
View File
@@ -3,11 +3,18 @@ from typing import List, Tuple, Type, Dict
import queue import queue
import threading import threading
import numpy as np import numpy as np
import torch
import time import time
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
import librosa IMPORT_FOUND = True
import pyaudio
try:
import torch
import librosa
import pyaudio
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
except ImportError:
print(Fore.RED + "Speech To Text disabled." + Fore.RESET)
IMPORT_FOUND = False
audio_queue = queue.Queue() audio_queue = queue.Queue()
done = False done = False
@@ -23,13 +30,18 @@ class AudioRecorder:
self.chunk = chunk self.chunk = chunk
self.record_seconds = record_seconds self.record_seconds = record_seconds
self.verbose = verbose self.verbose = verbose
self.audio = pyaudio.PyAudio() self.thread = None
self.thread = threading.Thread(target=self._record, daemon=True) self.audio = None
if IMPORT_FOUND:
self.audio = pyaudio.PyAudio()
self.thread = threading.Thread(target=self._record, daemon=True)
def _record(self) -> None: def _record(self) -> None:
""" """
Record audio from the microphone and add it to the audio queue. Record audio from the microphone and add it to the audio queue.
""" """
if not IMPORT_FOUND:
return
stream = self.audio.open(format=self.format, channels=self.channels, rate=self.rate, stream = self.audio.open(format=self.format, channels=self.channels, rate=self.rate,
input=True, frames_per_buffer=self.chunk) input=True, frames_per_buffer=self.chunk)
if self.verbose: if self.verbose:
@@ -58,10 +70,14 @@ class AudioRecorder:
def start(self) -> None: def start(self) -> None:
"""Start the recording thread.""" """Start the recording thread."""
if not IMPORT_FOUND:
return
self.thread.start() self.thread.start()
def join(self) -> None: def join(self) -> None:
"""Wait for the recording thread to finish.""" """Wait for the recording thread to finish."""
if not IMPORT_FOUND:
return
self.thread.join() self.thread.join()
class Transcript: class Transcript:
@@ -69,6 +85,9 @@ class Transcript:
Transcript is a class that transcribes audio from the audio queue and adds it to the transcript. Transcript is a class that transcribes audio from the audio queue and adds it to the transcript.
""" """
def __init__(self): def __init__(self):
if not IMPORT_FOUND:
print(Fore.RED + "Transcript: Speech to Text is disabled." + Fore.RESET)
return
self.last_read = None self.last_read = None
device = self.get_device() device = self.get_device()
torch_dtype = torch.float16 if device == "cuda" else torch.float32 torch_dtype = torch.float16 if device == "cuda" else torch.float32
@@ -91,6 +110,8 @@ class Transcript:
) )
def get_device(self) -> str: def get_device(self) -> str:
if not IMPORT_FOUND:
return "cpu"
if torch.backends.mps.is_available(): if torch.backends.mps.is_available():
return "mps" return "mps"
if torch.cuda.is_available(): if torch.cuda.is_available():
@@ -108,6 +129,8 @@ class Transcript:
def transcript_job(self, audio_data: np.ndarray, sample_rate: int = 16000) -> str: def transcript_job(self, audio_data: np.ndarray, sample_rate: int = 16000) -> str:
"""Transcribe the audio data.""" """Transcribe the audio data."""
if not IMPORT_FOUND:
return ""
if audio_data.dtype != np.float32: if audio_data.dtype != np.float32:
audio_data = audio_data.astype(np.float32) / np.iinfo(audio_data.dtype).max audio_data = audio_data.astype(np.float32) / np.iinfo(audio_data.dtype).max
if len(audio_data.shape) > 1: if len(audio_data.shape) > 1:
@@ -122,6 +145,9 @@ class AudioTranscriber:
AudioTranscriber is a class that transcribes audio from the audio queue and adds it to the transcript. AudioTranscriber is a class that transcribes audio from the audio queue and adds it to the transcript.
""" """
def __init__(self, ai_name: str, verbose: bool = False): def __init__(self, ai_name: str, verbose: bool = False):
if not IMPORT_FOUND:
print(Fore.RED + "AudioTranscriber: Speech to Text is disabled." + Fore.RESET)
return
self.verbose = verbose self.verbose = verbose
self.ai_name = ai_name self.ai_name = ai_name
self.transcriptor = Transcript() self.transcriptor = Transcript()
@@ -152,6 +178,8 @@ class AudioTranscriber:
""" """
Transcribe the audio data using AI stt model. Transcribe the audio data using AI stt model.
""" """
if not IMPORT_FOUND:
return
global done global done
if self.verbose: if self.verbose:
print(Fore.BLUE + "AudioTranscriber: Started processing..." + Fore.RESET) print(Fore.BLUE + "AudioTranscriber: Started processing..." + Fore.RESET)
@@ -185,9 +213,13 @@ class AudioTranscriber:
def start(self): def start(self):
"""Start the transcription thread.""" """Start the transcription thread."""
if not IMPORT_FOUND:
return
self.thread.start() self.thread.start()
def join(self): def join(self):
if not IMPORT_FOUND:
return
"""Wait for the transcription thread to finish.""" """Wait for the transcription thread to finish."""
self.thread.join() self.thread.join()
+11 -5
View File
@@ -5,9 +5,14 @@ import subprocess
from sys import modules from sys import modules
from typing import List, Tuple, Type, Dict from typing import List, Tuple, Type, Dict
from kokoro import KPipeline IMPORT_FOUND = True
from IPython.display import display, Audio try:
import soundfile as sf from kokoro import KPipeline
from IPython.display import display, Audio
import soundfile as sf
except ImportError:
print("Speech synthesis disabled. Please install the kokoro package.")
IMPORT_FOUND = False
if __name__ == "__main__": if __name__ == "__main__":
from utility import pretty_print, animate_thinking from utility import pretty_print, animate_thinking
@@ -33,7 +38,7 @@ class Speech():
} }
self.pipeline = None self.pipeline = None
self.language = language self.language = language
if enable: if enable and IMPORT_FOUND:
self.pipeline = KPipeline(lang_code=self.lang_map[language]) self.pipeline = KPipeline(lang_code=self.lang_map[language])
self.voice = self.voice_map[language][voice_idx] self.voice = self.voice_map[language][voice_idx]
self.speed = 1.2 self.speed = 1.2
@@ -57,7 +62,7 @@ class Speech():
sentence (str): The text to convert to speech. Will be pre-processed. sentence (str): The text to convert to speech. Will be pre-processed.
voice_idx (int, optional): Index of the voice to use from the voice map. voice_idx (int, optional): Index of the voice to use from the voice map.
""" """
if not self.pipeline: if not self.pipeline or not IMPORT_FOUND:
return return
if voice_idx >= len(self.voice_map[self.language]): if voice_idx >= len(self.voice_map[self.language]):
pretty_print("Invalid voice number, using default voice", color="error") pretty_print("Invalid voice number, using default voice", color="error")
@@ -159,6 +164,7 @@ class Speech():
if __name__ == "__main__": if __name__ == "__main__":
# TODO add info message for cn2an, jieba chinese related import # TODO add info message for cn2an, jieba chinese related import
IMPORT_FOUND = False
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
speech = Speech() speech = Speech()
tosay_en = """ tosay_en = """