# edge-tts-rust

High-performance, async-first Rust client for Microsoft Edge Read Aloud TTS.

## Features

- Async streaming synthesis for long text and backend workloads
- Voice listing, rate, volume, pitch, and word/sentence boundary timestamps
- Library API and simple CLI
- Unit tests, integration tests, and optional live tests

## Build

```bash
cargo build --release
```

## CLI

```bash
cargo run -- --text "Hello, world" --voice en-US-EmmaMultilingualNeural --write-media out.mp3 --write-subtitles out.srt
```

List voices:

```bash
cargo run -- --list-voices
```

## Library Usage

```rust
use edge_tts_rust::{Boundary, EdgeTtsClient, SpeakOptions};

#[tokio::main]
async fn main() -> edge_tts_rust::Result<()> {
    let client = EdgeTtsClient::new()?;
    let result = client
        .synthesize(
            "Hello from Rust",
            SpeakOptions {
                voice: "en-US-EmmaMultilingualNeural".into(),
                boundary: Boundary::Sentence,
                ..SpeakOptions::default()
            },
        )
        .await?;

    println!("audio bytes: {}", result.audio.len());
    println!("boundaries: {}", result.boundaries.len());
    Ok(())
}
```

Streaming:

```rust
use edge_tts_rust::{EdgeTtsClient, SpeakOptions, SynthesisEvent};
use futures_util::StreamExt;

#[tokio::main]
async fn main() -> edge_tts_rust::Result<()> {
    let client = EdgeTtsClient::new()?;
    let mut stream = client
        .stream("hello from rust", SpeakOptions::default())
        .await?;

    while let Some(event) = stream.next().await {
        match event? {
            SynthesisEvent::Audio(chunk) => println!("audio chunk: {}", chunk.len()),
            SynthesisEvent::Boundary(boundary) => println!("boundary: {}", boundary.text),
        }
    }
    Ok(())
}
```

## Testing

```bash
cargo test
```

Run the optional live test:

```bash
EDGE_TTS_ONLINE_TEST=1 cargo test --test live
```

Run benchmarks:

```bash
cargo bench
```
