# 如何使用文本转语音（TTS）和语音转文本（STT）

> 朗读一段文本并将用户的语音转录为文本，可配置区域设置/速度。

Source: https://crossfly.dev/zh/docs/cookbook/device/tts-stt — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 如何使用文本转语音（TTS）和语音转文本（STT）

**目的。** 让设备**朗读**一段文本（TTS）并把用户的语音**转录**为
文本（STT）。支持：Android 和 iOS。

## 使用的 API
- `Device.textToVoice(String text)` — 合成并把 `text` 作为语音播放。**同一个字符串还可用于配置**引擎，通过以逗号分隔的 `chave:valor` 键值对。
  - `locale:por_BRA` — 语言代码（例如 `por_BRA`、`eng_USA_f00`）。
  - `speech:0.75` — 速度倍数（`1.0` = 正常，`0.5` = 慢，`2.0` = 快）。
  - `languages` — 在 logcat（调试）中列出可用语言。`quit` — 关闭 TTS 引擎。
- `Device.voiceToText(VoiceToTextListener listener)` — 启动语音识别；结果通过回调到达。
- `Device.VoiceToTextListener` — `onStart()`、`onFinish()`、`onResult(String text)`、`onError(int code, String msg)`。
- `Device.soundToSpeechTimeout`（静态，毫秒，默认 `5000`）— 识别的超时时间。

> **是的，`textToVoice` 接受配置字符串。** 首次调用可以配置
> `locale`/`speech`（例如 `"locale:por_BRA,speech:0.75"`）；后续携带普通文本的调用则朗读
> 内容。`voiceToText` 的回调会**在 paint thread 之外**到达 → 更新 UI 时请使用 `Component.onUI(...)`。
> 在 Android 上首次执行时可能会提示安装 HD
> 语言包。

## 完整示例（复制、粘贴并运行）

```java
package com.exemplo;

import dev.crossfly.ui.*;                   // Window, Component, Button, Label
import dev.crossfly.ui.layout.*;            // VerticalLayout
import dev.crossfly.device.*;               // Device, Device.VoiceToTextListener

/** 可执行的主界面。将 launcher 指向此类（classname）并运行：gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      Label resultado = new Label("");
      add(LAY_AUTO, resultado);

      Button falar = new Button("Falar (TTS)");
      falar.onAction(c ->
      {
         // TTS — 配置语言/语速后再朗读
         Device.textToVoice("locale:por_BRA,speech:0.9");     // 配置（可选，首次）
         Device.textToVoice("Olá! Bem-vindo ao aplicativo.");  // 朗读文本
         resultado.setText("Falando...");
      });
      add(LAY_AUTO, falar);

      Button ouvir = new Button("Transcrever (STT)");
      ouvir.onAction(c ->
      {
         // STT — 转写用户的语音（回调在 paint thread 之外）
         Device.voiceToText(new Device.VoiceToTextListener()
         {
            public void onStart()  { Component.onUI(() -> resultado.setText("Ouvindo...")); }
            public void onFinish() { Component.onUI(() -> resultado.setText("Pronto")); }
            public void onResult(String text)
            {
               Component.onUI(() -> resultado.setText("Reconhecido: " + text));
            }
            public void onError(int code, String msg)
            {
               Component.onUI(() -> resultado.setText("Erro: " + msg));
            }
         });
      });
      add(LAY_AUTO, ouvir);
   }
}
```

> 下面的片段仅展示**核心部分**。要运行它，请把它放入上面的 `MainWindow` 骨架中。

## 最小片段

```java
// 已在源码验证：Device.java (textToVoice 第 387 行, voiceToText 第 340 行, VoiceToTextListener 第 397 行)
import dev.crossfly.device.*;              // Device, Device.VoiceToTextListener
import dev.crossfly.ui.*;                  // Component (onUI)

// 1) TTS — 配置语言/语速后再朗读
Device.textToVoice("locale:por_BRA,speech:0.9");   // 配置（可选，首次）
Device.textToVoice("Olá! Bem-vindo ao aplicativo."); // 朗读文本

// 2) STT — 转写用户的语音（回调在 paint thread 之外）
Device.voiceToText(new Device.VoiceToTextListener() {
   public void onStart()  { Component.onUI(() -> mostrarStatus("Ouvindo...")); }
   public void onFinish() { Component.onUI(() -> mostrarStatus("Pronto")); }
   public void onResult(String text) {
      Component.onUI(() -> preencherCampo(text));   // 识别出的文本
   }
   public void onError(int code, String msg) {
      Component.onUI(() -> mostrarErro(msg));
   }
});
```

## 深入了解
- 声音与杂项指南：[声音与杂项](/docs/data-and-services/device-sound-and-misc)
- 姊妹配方：[vibrar-beep-clipboard.md](/docs/cookbook/device/vibrar-beep-clipboard)
