# Working with audio and images

> **What you will learn**
> How to use the audio and image paths, and the one thing that only works here.

## Why these get their own chapter

Chat and embeddings in [the previous chapter](/guide/ht-model-api) return
**text.** Audio and images return **files**, which changes how you handle them.

- Responses are large (wav, png)
- They take a long time
- **You have to decide where the result goes**

## Text to speech

```bash
curl -X POST http://localhost:27777/api/ai/v1/audio/speech \
  -H "Authorization: Bearer $HT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<model name>",
    "input": "Hello. Here is today'\''s update.",
    "voice": "<voice>"
  }' \
  --output out.wav
```

**Forget `--output` and binary data floods your terminal.** A common mistake.

### Split long text

Feeding in a long script at once takes a while, and a failure midway means
starting over. **Generating per paragraph and joining** is more robust.

```
✗ the whole script at once
✓ one wav per paragraph → a file list → join
```

Joining is a job for an audio tool in [the terminal](/guide/ht-terminal).

## Speech to text

```bash
curl -X POST http://localhost:27777/api/ai/v1/audio/transcriptions \
  -H "Authorization: Bearer $HT_KEY" \
  -F file=@meeting.m4a \
  -F model="<model name>"
```

This is the first step of **turning a meeting recording into meeting notes.**

```mermaid
graph TD
  A["Recording"] --> B["Speech → text"]
  B --> C["Raw transcript"]
  C --> D["Hand to a task<br/>'organise into action items'"]
  D --> E["Meeting notes"]
```

The second step is creating a task via
[the REST API](/guide/ht-rest-api). **Joining the model API to the task API**
like this is the shape you will use most often in practice.

## Cloned voices — only here

This is not in the OpenAI specification, so its path is not under `/v1/` either.

```
/api/voice-profiles
```

Register your own voice, or a chosen one, and select it as the `voice`. It lets
you produce **many pieces consistently in the same voice.**

> **⚠ A voice is personal data.**
> Cloning someone else's voice requires their consent. That holds for internal
> material too, and more so for anything published. **Do not make one without
> consent.**

## Image generation

```bash
curl -X POST http://localhost:27777/api/ai/v1/images/generations \
  -H "Authorization: Bearer $HT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<model name>",
    "prompt": "a single glass capsule floating, centered, dark background"
  }'
```

The response carries image addresses. Downloading and saving them is a separate
step.

### Generating in batches

For many images, **keep the prompts in a file.**

```json
[
  { "subject": "...", "composition": "centered", "mood": "solemn" },
  { "subject": "...", "composition": "low angle", "mood": "tense" }
]
```

That buys you three things.

| Reason | |
|---|---|
| Reproducibility | you can produce the same result again |
| Selective redo | regenerate only the ones you dislike |
| Consistency | apply style fields across all of them |

**Bury the prompts in code and you lose all three.**

### Editing and upscaling

| Path | Use |
|---|---|
| `/v1/images/inpainting` | redraw part of it |
| `/v1/images/upscale` | raise the resolution |

**Generate many at low resolution, upscale only the keepers** — better on both
time and cost.

## Where the output files go

This is what tangles most often in practice.

```
working-directory/
  prompts.json      ← input (version controlled)
  out/              ← output (excluded from version control)
    img-001.png
    voice-001.wav
```

| Principle | Why |
|---|---|
| Separate input and output by folder | what to delete on a rerun is obvious |
| Exclude output from git | binaries bloat the repository |
| Sequence numbers or dates in filenames | essential where order matters |

## It takes a long time

| Work | Feel |
|---|---|
| A short sentence of speech | seconds |
| A long script | minutes |
| One image | seconds to minutes |
| Dozens of images | **ten minutes or more** |

So work like this is better **run on [a schedule](/guide/ht-pipelines) with only
the result checked.** It is not something to sit and wait for.

## Common mistakes

### Regenerating everything after a partial failure

Three of fifty failed and the whole batch runs again. **Make it skip files that
already exist.** That requires predictable filenames.

### Not recording the prompt

A great image comes out and you do not know which prompt made it. **Keep the
input file alongside.**

### Not getting voice consent

Written above, but worth repeating. **Cloned voices presume consent.**

---

## Check yourself

**1. Why split a long script for speech?**

<details>
<summary>Answer</summary>

**Because all at once takes a while and a failure midway means starting over.**
Per paragraph, you regenerate only what failed.
</details>

**2. What do you gain by keeping image prompts in a file?**

<details>
<summary>Answer</summary>

**Reproducibility, selective redo, and consistency.** You can make the same
result again, regenerate only what you dislike, and apply style fields across
everything.
</details>

**3. What must you confirm before using a cloned voice?**

<details>
<summary>Answer</summary>

**The consent of the voice's owner.** A voice is personal data, and cloning
without consent is not acceptable even for internal material.
</details>

---

Now make all of this run by itself →
[Building pipelines with schedules](/guide/ht-pipelines)
