Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ manifest.ini
*.py[co]
*.nvda-addon
.sconsign.dblite
.agent/
.venv/
.ruff_cache/
/[0-9]*.[0-9]*.[0-9]*.json
99 changes: 99 additions & 0 deletions addon/doc/es/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Native Speech Generation para NVDA

**Autor:** Muhammad Gagah [muha.aku@gmail.com](mailto:muha.aku@gmail.com)

Native Speech Generation es un complemento para NVDA que integra **Google Gemini AI** para generar voz de alta calidad y sonido natural directamente en NVDA. Proporciona una interfaz limpia y totalmente accesible para convertir texto en audio, admitiendo tanto **narración de un solo hablante** como **diálogos dinámicos de varios hablantes**.

Este complemento está diseñado para flujos de trabajo fluidos, interacción centrada en la accesibilidad y un control de voz flexible adecuado para narración, diálogos y producción de contenido de audio.

---

## Características

### Generación de voz de alta calidad
* **Elija entre:**
* **Gemini Flash:** Calidad estándar, generación rápida, baja latencia.
* **Gemini Pro:** Premium, voces más realistas (modelo de pago).

### Modos de hablante único y múltiple
* **Narración de hablante único** para conversión de texto a voz estándar.
* **Modo multihablante (2 hablantes)** para diálogos con voces distintas.

### Control de voz avanzado
* **Asignación de nombres a los hablantes:** Asigne nombres personalizados (ej. *Juan*, *María*) en el modo multihablante. La IA asigna automáticamente las voces según los nombres en el guion.
* **Instrucciones de estilo:** Proporcione indicaciones como *"Habla en un tono alegre"* o *"Narra con calma"* para guiar el habla.
* **Control de temperatura:** Ajuste la variación y creatividad de la salida:
* Valores bajos → voz más estable y predecible.
* Valores altos → voz más expresiva y variada.

---

## Requisitos
* NVDA (se recomienda la versión más reciente).
* Conexión a internet activa.
* Una **clave de API de Google Gemini** válida.

---

## Instalación
1. Descargue el paquete desde la [página de versiones (Releases)](https://github.com/MuhammadGagah/native-speech-generation/releases).
2. Instálelo como cualquier complemento de NVDA.
3. Reinicie NVDA cuando se le solicite.

---

## Configuración de la clave de API (Requerido)
1. Cree una clave en [Google AI Studio](https://aistudio.google.com/apikey).
2. Vaya a: **Menú NVDA → Herramientas → Native Speech Generation**.
3. Haga clic en **"Configuración de clave API"**.
4. Pegue su clave en el campo **GEMINI API Key**.
5. Haga clic en **Aceptar**.

---

## Cómo usar
Abra el diálogo usando:
* **NVDA+Control+Mayús+G**, o
* **Menú NVDA → Herramientas → Native Speech Generation**

### Elementos de la interfaz
* **Texto a convertir:** Ingrese o pegue su texto.
* **Instrucciones de estilo:** (Opcional) Guía para el tono o emoción.
* **Seleccionar modelo:** Flash o Pro.
* **Modo de hablante:** Único o Multihablante (2).

---

## Generación de voz

### Modo de hablante único
1. Seleccione **Hablante único**.
2. Elija una voz, ingrese el texto y haga clic en **Generar voz**.

### Modo multihablante
1. Seleccione **Multihablante (2)**.
2. Asigne un nombre y voz a cada hablante.
3. Formatee el texto así:
`Alicia: Hola Roberto, ¿cómo estás?`
`Roberto: ¡Muy bien! El clima es genial.`

---

## Hablar con IA (Modo en vivo)
1. Haga clic en **Hablar con IA**.
2. **Iniciar conversación:** Comience a hablar por el micrófono.
3. **Conexión con Google Search:** Marque esta casilla para información en tiempo real.

---

## Gestos de entrada
Personalizables en: **Menú NVDA → Preferencias → Gestos de entrada**.
* Predeterminado: **NVDA+Control+Mayús+G**

---

## Contribuciones
* Reporte errores en la sección de **Issues**.
* Envíe mejoras mediante **Pull Requests**.

**Contacto:** [muha.aku@gmail.com](mailto:muha.aku@gmail.com) | [GitHub](https://github.com/MuhammadGagah)
5 changes: 5 additions & 0 deletions addon/doc/id/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ Add-on ini dirancang untuk alur kerja yang lancar, interaksi yang mengutamakan a
* **Grounding dengan Pencarian Google**: Mengaktifkan AI untuk mengakses informasi real-time dari web selama obrolan Anda.
* **Dapat Diinterupsi**: Anda dapat memotong pembicaraan AI kapan saja dengan berbicara atau menekan tombol "Hentikan Percakapan".
* **Dapat Disesuaikan**: Menggunakan suara dan instruksi gaya yang Anda pilih.
* **Memori Sesi (Opsional)**: Menyimpan konteks percakapan sementara selama sesi Bicara dengan AI aktif agar respons lanjutan lebih koheren.
* **Kontrol Hapus Memori**: Menghapus memori sesi sementara sebelum percakapan dimulai (atau setelah percakapan dihentikan).
* **Streaming Lebih Stabil**: Perbaikan reconnect (backoff + retry) dan adaptive audio buffering agar lebih tahan terhadap jaringan yang tidak stabil.

---

Expand Down Expand Up @@ -157,6 +160,8 @@ Rasakan percakapan suara dua arah yang alami dengan Gemini.
* **Hentikan Percakapan**: Mengakhiri sesi.
* **Grounding dengan Google Search**: Centang kotak ini untuk mengizinkan Gemini menelusuri web guna mencari jawaban (misalnya, berita terkini, cuaca).
* *Catatan: Kotak centang ini disembunyikan saat percakapan sedang aktif. Hentikan percakapan untuk mengubahnya.*
* **Gunakan memori sesi**: Menyimpan memori sementara hanya untuk sesi percakapan saat ini.
* **Hapus memori sekarang**: Menghapus memori sementara sebelum memulai (atau setelah menghentikan) percakapan.
* **Tombol Mikrofon**: Bisukan/Bunyikan mikrofon Anda.
* **Volume**: Sesuaikan volume pemutaran AI.

Expand Down
11 changes: 2 additions & 9 deletions addon/globalPlugins/NativeSpeechGeneration/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
import os
import sys
import wx
import addonHandler
import globalPluginHandler
Expand All @@ -23,11 +22,6 @@ def _(msg: str) -> str:
# Initialization & Dependency Management
pkgDir = os.path.dirname(os.path.abspath(__file__))

# Ensure globalPlugins is in path
gpDir = os.path.dirname(pkgDir)
if gpDir not in sys.path:
sys.path.insert(0, gpDir)

# Libs setup
try:
from . import lib_updater
Expand All @@ -53,8 +47,6 @@ def runCheck() -> None:

wx.CallAfter(runCheck)
else:
if libDir not in sys.path:
sys.path.insert(0, libDir)
LIBS_AVAILABLE = True

if not LIBS_AVAILABLE:
Expand All @@ -74,7 +66,8 @@ def script_openDialog(self, gesture: Any) -> None:
wx.CallAfter(
wx.MessageBox,
_(
"Native Speech Generation is installing dependencies. Please restart NVDA for the changes to take effect.",
"Native Speech Generation is installing dependencies. "
"Please restart NVDA for the changes to take effect.",
),
# Translators: Title of the information dialog recommending a restart.
_("Restart Required"),
Expand Down
73 changes: 33 additions & 40 deletions addon/globalPlugins/NativeSpeechGeneration/core/gemini_imports.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,36 @@
# -*- coding: utf-8 -*-
import sys
import platform
import os

from logHandler import log

GENAI_AVAILABLE = False
genai = None
types = None

__all__ = ["genai", "types", "GENAI_AVAILABLE"]

try:
# Dependency Conflict Resolution (Scoped / Safe Mode)
conflictingLibs = ["typing_extensions", "pydantic", "pydantic_core", "annotated_types"]
originalModules = {}

for lib in conflictingLibs:
if lib in sys.modules:
originalModules[lib] = sys.modules[lib]
del sys.modules[lib]
try:
from google import genai
from google.genai import types

GENAI_AVAILABLE = True
log.info("google-genai loaded successfully via core.gemini_imports")
finally:
# Restore original modules to avoid breaking other add-ons
for lib, module in originalModules.items():
sys.modules[lib] = module
except Exception as e:
genai = None
types = None
GENAI_AVAILABLE = False

errMsg = (
f"Google GenAI Import Error:\n{e}\n\n"
f"Python: {sys.version}\n"
f"Arch: {platform.architecture()}\n"
f"Path: {sys.path[:3]}..."
)
log.warning("google-genai not available", exc_info=True)
log.error(errMsg)
from .vendor_loader import load_runtime, runtime_scope

_CORE_DIR = os.path.dirname(os.path.abspath(__file__))
_PKG_DIR = os.path.dirname(_CORE_DIR)
_LIB_DIR = os.path.join(_PKG_DIR, "lib")
_RUNTIME = load_runtime(_LIB_DIR)

genai = _RUNTIME.genai
types = _RUNTIME.types
pyaudio = _RUNTIME.pyaudio

GENAI_AVAILABLE = _RUNTIME.genaiAvailable
PYAUDIO_AVAILABLE = _RUNTIME.pyaudioAvailable

__all__ = [
"genai",
"types",
"pyaudio",
"GENAI_AVAILABLE",
"PYAUDIO_AVAILABLE",
"getRuntimeScope",
]

if not GENAI_AVAILABLE:
log.warning("google-genai not available via isolated vendor loader")
if not PYAUDIO_AVAILABLE:
log.warning("PyAudio not available via isolated vendor loader")


def getRuntimeScope():
return runtime_scope(_RUNTIME)
Loading