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 @@ -13,6 +13,9 @@ __pycache__/
.env.local
!.env.example

# Voice-cloning consent challenge cache (single-use, per-run)
.consent-challenge.json

# Generated audio / artifacts
*.mp3
*.wav
Expand Down
2 changes: 1 addition & 1 deletion agents/python-recipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ name = "audio-python-quickstart"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = [
"speechify-api>=3.0.1",
"speechify-api>=4.0.0",
"python-dotenv>=1.0.0",
]
```
Expand Down
20 changes: 10 additions & 10 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ packages:
# Single source of truth for shared dependency versions.
# Recipes reference these with "catalog:" instead of pinning their own.
catalog:
"@speechify/api": 3.0.1
"@speechify/api": 4.0.1
tsx: ^4.19.2
typescript: ^5.7.2
"@types/node": ^22.10.2
Expand Down
2 changes: 1 addition & 1 deletion recipes/audio/python/sdk/quickstart/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ version = "0.1.0"
description = "Synthesize speech to an MP3 file with the Speechify TTS API."
requires-python = ">=3.10"
dependencies = [
"speechify-api>=3.0.1",
"speechify-api>=4.0.0",
"python-dotenv>=1.0.0",
]
8 changes: 4 additions & 4 deletions recipes/audio/python/sdk/quickstart/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion recipes/audio/python/sdk/speech-marks/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ version = "0.1.0"
description = "Generate WebVTT captions from word-level speech marks with the Speechify TTS API."
requires-python = ">=3.10"
dependencies = [
"speechify-api>=3.0.1",
"speechify-api>=4.0.0",
"python-dotenv>=1.0.0",
]
8 changes: 4 additions & 4 deletions recipes/audio/python/sdk/speech-marks/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion recipes/audio/python/sdk/ssml-emotion/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ version = "0.1.0"
description = "Control emotion, pitch, rate, pauses & emphasis via SSML with the Speechify TTS API."
requires-python = ">=3.10"
dependencies = [
"speechify-api>=3.0.1",
"speechify-api>=4.0.0",
"python-dotenv>=1.0.0",
]
8 changes: 4 additions & 4 deletions recipes/audio/python/sdk/ssml-emotion/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion recipes/audio/python/sdk/streaming/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ version = "0.1.0"
description = "Stream synthesized audio to a file as it is generated with the Speechify TTS API."
requires-python = ">=3.10"
dependencies = [
"speechify-api>=3.0.1",
"speechify-api>=4.0.0",
"python-dotenv>=1.0.0",
]
8 changes: 4 additions & 4 deletions recipes/audio/python/sdk/streaming/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions recipes/audio/python/sdk/voice-cloning/.env.example
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
# Get a key at https://platform.speechify.ai/api-keys
# Voice cloning must be enabled on your plan.
SPEECHIFY_API_KEY=

# Full name of the person consenting to have their voice cloned.
CONSENT_FULL_NAME=Jane Doe

# Optional — override where the recipe reads the audio from.
# Defaults to sample.wav and consent.wav in the recipe folder.
# SAMPLE_PATH=./sample.wav
# CONSENT_RECORDING_PATH=./consent.wav
41 changes: 28 additions & 13 deletions recipes/audio/python/sdk/voice-cloning/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Text-to-Speech: voice cloning (Python)

Clone a voice from an audio sample, synthesize speech with the clone, then delete it — the
full create → use → delete lifecycle.
full create → use → delete lifecycle, with **verified consent**.

## Prerequisites

Expand All @@ -10,6 +10,9 @@ full create → use → delete lifecycle.
pointing to [Speechify pricing](https://speechify.ai/pricing) (the API returns
`402 voice_cloning_not_included`).
- Python 3.10+ and [uv](https://docs.astral.sh/uv/)
- **Two recordings of the same consenting person** (see [Consent](#consent)):
- a voice **sample** to clone — 10–30s of clean speech
- a **consent recording** — that person reading the challenge phrase this recipe prints

## Setup

Expand All @@ -20,29 +23,41 @@ uv sync

## Run

Because consent is verified against a phrase the API generates, this is a two-step run:

```bash
uv run main.py
uv run main.py # 1st run: prints the phrase to read, then exits
# record the speaker reading that phrase → consent.wav, and their voice → sample.wav
uv run main.py # 2nd run: clones, synthesizes, deletes
```

Produces an `output.mp3` spoken in the cloned voice, then removes the cloned voice.
Put `sample.wav` and `consent.wav` in the recipe folder, or point `SAMPLE_PATH` /
`CONSENT_RECORDING_PATH` at them. Produces an `output.mp3` in the cloned voice, then
removes the cloned voice.

## What it does

- `client.voices.create(...)` — clones a voice from `fixtures/spacewalk.wav`. Required
fields: `name`, `gender`, `sample` (a readable binary file of 10–30s of clean speech),
and `consent`.
- `client.voices.consent_challenges.create(full_name=...)` — starts a consent challenge and
returns a `phrase` the speaker must read aloud (cached in `.consent-challenge.json`
between runs; single-use).
- `client.voices.create(...)` — clones the voice. Required: `name`, `gender`, `sample` (a
readable binary file of 10–30s of clean speech), `consent_challenge_id`, and
`consent_recording` (the speaker reading the phrase).
- `client.audio.speech(...)` with `voice_id` set to the new voice's id.
- `client.voices.delete(id)` — cleans up so personal voices don't accumulate.
- `client.voices.delete(voice_id)` — cleans up so personal voices don't accumulate.

## Consent

`consent` is a **required** JSON string attesting you have the speaker's permission to
clone their voice (`{"fullName": "...", "email": "..."}`). Only clone voices you are
authorized to. The bundled `fixtures/spacewalk.wav` is ~26s of NASA ISS spacewalk audio
(U.S. government work, public domain); replace it with your own consented sample for real
use.
Cloning requires **verified consent**. You create a consent challenge, the speaker records
themselves reading the returned `phrase`, and that recording is sent as `consent_recording`
and retained as the consent record. The `consent_recording` **must be the same person** as
the voice `sample` — so there is no shippable sample that comes with valid consent, and this
recipe is deliberately bring-your-own-audio. Only clone voices you are authorized to.

> The old `consent` JSON field (`fullName` + `email`) is removed in SDK 4.x — see the
> [migration guide](https://docs.speechify.ai/build/guides/deprecations/migrating-voice-cloning-consent).
>
> Note: `voices.list()` is eventually consistent — a just-deleted voice may still appear in
> the list briefly. The delete itself is immediate (a subsequent delete returns 404).
>
> Voice cloning reference: https://docs.speechify.ai/tts/guides/voice-cloning
> Voice cloning reference: https://docs.speechify.ai/build/guides/voice-cloning/overview
Binary file not shown.
Loading
Loading