Metadata-Version: 2.4
Name: VoiceConductor
Version: 0.1.0
Summary: Importable text-to-speech package with Windows audio routing support.
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: msgpack>=1.0.7
Requires-Dist: numpy>=1.26
Requires-Dist: pypercache>=0.1.4
Requires-Dist: sounddevice>=0.4.6
Provides-Extra: elevenlabs
Provides-Extra: kokoro
Requires-Dist: kokoro>=0.9.4; extra == "kokoro"

# VoiceConductor

A Python package for generating and routing synthesized voice lines. Supports output to speakers or virtual microphones.

**Entry point:** `TTSManager`

## Features

- Provider fallback across ElevenLabs, Kokoro, Azure Speech, Windows Speech, and the built-in demo provider.
- SQLite phrase caching so repeated lines do not need to be synthesized again.
- Named playback routes for speakers and virtual mic devices.
- Background playback tasks for non-blocking speech.
- Playback lifecycle hooks for push-to-talk workflows.
- JSON/JSONC configuration for provider credentials, default voices, route settings, and cache paths.

## Requirements

- Python 3.11 or newer.
- Audio playback support via `sounddevice`.
- Optional provider dependencies and credentials depending on the backend you want to use.

## Installation

Install the package:

```bash
pip install VoiceConductor
```

Install with Kokoro support:

```bash
pip install "VoiceConductor[kokoro]"
```

For local development from a checkout:

```bash
pip install -e ".[kokoro]"
```

## Quick Start

```python
from voice_conductor import TTSManager

tts = TTSManager()
tts.speak("This is a test.", routes="speakers")
```

Route to a virtual mic:

```python
from voice_conductor import TTSManager

tts = TTSManager()
tts.speak("Now, to the virtual microphone.", routes="mic")
```

Route to both speakers and mic:

```python
tts.speak("Routed to both output devices.", routes=["speakers", "mic"])
```

Synthesize once, then route the resulting audio:

```python
audio = tts.synthesize_voice("This audio sample is stored in audio.")
result = tts.route(audio, routes=["speakers", "mic"])
print(result.routes)
```

## Configuration

By default, `voice_conductor` looks for one of these files in the current working directory:

- `voice_conductor.config.jsonc`
- `voice_conductor.config.json`

If neither file exists, defaults are used. To create a config file you can edit, save the current settings:

```python
from voice_conductor import load_settings

settings = load_settings()
settings.save_settings("voice_conductor.config.jsonc")
```

Provider selection follows `voice_conductor.provider_chain`. When `speak()` or `synthesize_voice()` does not specify a provider, the manager uses the first available provider in that chain.

## Providers

Built-in providers:

| Provider | Use case | Availability |
| --- | --- | --- |
| `elevenlabs` | Hosted high-quality voices. | Requires API key. |
| `kokoro` | Local Kokoro synthesis. | Requires the `kokoro` extra and model access. |
| `azure` | Azure neural voices. | Requires Speech key and region. |
| `windows` | Installed Windows System.Speech voices. | Requires Windows speech support. |
| `demo` | Offline test voice. | No external service. |

List available providers:

```python
from voice_conductor import TTSManager

tts = TTSManager()
print(tts.list_providers())
```

List voices for a provider:

```python
for voice in tts.list_voices("windows"):
    print(voice.id, voice.name)
```

## Audio Routes

Routes are named outputs. The default route names are:

- `speakers`
- `mic`

You can pass one route name or a list of route names:

```python
tts.speak("Hello, world!", routes="speakers")
tts.speak("Hello, world!", routes=["speakers", "mic"])
```

The `mic` route resolves to an output device because virtual microphone tools such as VB-CABLE and VoiceMeeter expose playback endpoints that chat apps receive as microphone input. See `docs/mic-setup.md` for a virtual microphone setup guide.

## Cache

Synthesized phrases are cached in SQLite. Cache entries are keyed by:

- text
- provider
- normalized voice key
- provider settings that affect audio output

Useful cache methods:

```python
tts.invalidate_synthesis_cache(text="Hello, world!")
tts.invalidate_synthesis_cache(provider="elevenlabs")
tts.clear_synthesis_cache()
```

Pass `refresh_cache=True` to regenerate a phrase and replace the cached entry:

```python
tts.speak("New take.", refresh_cache=True)
```

## Background Playback

Use `background=True` when the caller should continue immediately:

```python
task = tts.speak("Now we're not blocking the main thread.", routes="mic", background=True)
result = task.result(timeout=10)
```

## Push-To-Talk Hooks

Playback hooks run after audio and routes are ready and after playback completes. They are useful for pressing and releasing push-to-talk around virtual mic playback.

```python
from voice_conductor import PlaybackHooks, TTSManager

tts = TTSManager()

tts.speak(
    "Check out the playback hooks.",
    routes="mic",
    hooks=PlaybackHooks(
        on_audio_ready=lambda event: press_push_to_talk(),
        on_playback_complete=lambda event: release_push_to_talk(),
    ),
)
```

## Documentation

- `docs/general.md`: architecture, settings, providers, and shared types.
- `docs/mic-setup.md`: virtual microphone setup and troubleshooting.
- `examples/`: small runnable examples.

## Development

Install the project locally:

```bash
pip install -e ".[kokoro]"
```

Run tests:

```bash
pytest
```

Run a focused test file:

```bash
pytest tests/test_manager.py
```
