Skip to content

TTS (文本生成语音)

在算力舱中部署了一个 Kokorotts 服务,这个服务的速度比较快。

如果您需要 OpenAI 兼容的多音色 TTS 服务,可以参考 Qwen3TTS API

qwen3tts-ai 的服务入口可以通过 访问。

在线演示

您可以通过 访问在线演示。

文档

tts 服务的 api 可以通过 查看。

Qwen3TTS 服务接口说明见 Qwen3TTS API

如果你要接 OpenAI 风格的 POST /v1/audio/speech,或者需要查询 /v1/audio/voices、使用 instruct / language 扩展参数、按 /health 中的 ready=true 做启动校验,应使用 qwen3tts-ai,而不是当前这一页介绍的 tts-ai

qwen3tts-ai 当前文档里也明确说明了:

  • OpenAI 接口已支持请求级 language
  • OpenAI 接口已支持请求级 instruct
  • OpenAI 接口已支持请求级 seed
  • 同一镜像版本、同一模型和同一请求参数下,固定 seed 可以复现相同音频输出
  • 当前文档只保留实际对外建议使用的请求字段;像 speed 这类兼容但未生效的字段不再作为正式参数介绍

单独部署使用

  1. 将下面内容保存到一个 docker-compose.yml 文件中
yml
services:
  tts:
    image: registry.lazycat.cloud/x/lzc-aipod-tts:763ebf7
    ports:
      - 8880:8880
    environment:
      - PYTHONPATH=/app:/app/api
      - USE_GPU=true
      - PYTHONUNBUFFERED=1
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8880/health"]
      interval: 30s
      timeout: 5s
      retries: 100
      start_period: 1s
  1. 在当前目录运行 docker-compose up -d 启动
  2. 启动后访问 http://127.0.0.1:8880/docs
示例
vue
<template>
  <div class="tts-player">
    <div class="input-section">
      <textarea
        v-model="text"
        placeholder="请输入要转换的文本..."
        class="text-input"
        rows="4"
      ></textarea>
      <button
        @click="synthesizeSpeech"
        :disabled="isLoading"
        class="synthesize-btn"
      >
        {{ isLoading ? "转换中..." : "转换为语音" }}
      </button>
    </div>

    <div class="player-section" v-if="audioUrl">
      <audio
        ref="audioPlayer"
        :src="audioUrl"
        controls
        class="audio-player"
      ></audio>
    </div>
  </div>
</template>

<script setup>
import { ref } from "vue";

const text = ref("欢迎使用算力舱,玩的开心。welcome use AI pod, happy hack!");
const audioUrl = ref("");
const isLoading = ref(false);
const audioPlayer = ref(null);

const domain = () => {
  const currentHost = window.location.host;
  const host = currentHost.replace(/^([^.]+)/, "tts-ai");
  const protocol = window.location.protocol;
  return `${protocol}//${host}`;
};

const synthesizeSpeech = async () => {
  if (!text.value.trim()) {
    alert("请输入要转换的文本");
    return;
  }

  isLoading.value = true;
  try {
    const response = await fetch(`${domain()}/v1/audio/speech`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        input: text.value,
        voice: "zf_044",
        response_format: "mp3",
        download_format: "wav",
        stream: true,
        speed: 1,
        return_download_link: true,
      }),
    });

    if (!response.ok) {
      throw new Error("语音合成请求失败");
    }

    const blob = await response.blob();
    audioUrl.value = URL.createObjectURL(blob);

    // 自动播放
    if (audioPlayer.value) {
      audioPlayer.value.play();
    }
  } catch (error) {
    console.error("语音合成错误:", error);
    alert("语音合成失败,请重试");
  } finally {
    isLoading.value = false;
  }
};
</script>

<style>
.tts-player {
  margin: 0 auto;
}

.input-section {
  margin-bottom: 20px;
}

.text-input {
  width: 100%;
  padding: 12px;
  border: 1px solid #ddd;
  border-radius: 4px;
  margin-bottom: 10px;
  font-size: 16px;
  resize: vertical;
}

.synthesize-btn {
  background-color: #4caf50;
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 16px;
  transition: background-color 0.3s;
}

.synthesize-btn:hover {
  background-color: #45a049;
}

.synthesize-btn:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}

.player-section {
  margin-top: 20px;
}

.audio-player {
  width: 100%;
}
</style>