此版本仍在开发中,尚未被视为稳定版。如需最新的快照版本,请使用 Spring AI 1.1.3spring-doc.cadn.net.cn

文本转语音 (TTS) API

Spring AI 通过TextToSpeechModelStreamingTextToSpeechModel接口为文本转语音(TTS)提供了统一的 API。这使您能够编写可移植的代码,以适配不同的 TTS 提供商。spring-doc.cadn.net.cn

公共接口

所有 TTS 提供商都实现了以下共享接口:spring-doc.cadn.net.cn

TextToSpeechModel

TextToSpeechModel 接口提供了将文本转换为语音的方法:spring-doc.cadn.net.cn

public interface TextToSpeechModel extends Model<TextToSpeechPrompt, TextToSpeechResponse>, StreamingTextToSpeechModel {

    /**
     * Converts text to speech with default options.
     */
    default byte[] call(String text) {
        // Default implementation
    }

    /**
     * Converts text to speech with custom options.
     */
    TextToSpeechResponse call(TextToSpeechPrompt prompt);

    /**
     * Returns the default options for this model.
     */
    default TextToSpeechOptions getDefaultOptions() {
        // Default implementation
    }
}

流式文本转语音模型

StreamingTextToSpeechModel 接口提供了用于实时流式传输音频的方法:spring-doc.cadn.net.cn

@FunctionalInterface
public interface StreamingTextToSpeechModel extends StreamingModel<TextToSpeechPrompt, TextToSpeechResponse> {

    /**
     * Streams text-to-speech responses with metadata.
     */
    Flux<TextToSpeechResponse> stream(TextToSpeechPrompt prompt);

    /**
     * Streams audio bytes for the given text.
     */
    default Flux<byte[]> stream(String text) {
        // Default implementation
    }
}

文本转语音提示

TextToSpeechPrompt 类封装了输入文本和选项:spring-doc.cadn.net.cn

TextToSpeechPrompt prompt = new TextToSpeechPrompt(
    "Hello, this is a text-to-speech example.",
    options
);

文本转语音响应

TextToSpeechResponse 类包含生成的音频和元数据:spring-doc.cadn.net.cn

TextToSpeechResponse response = model.call(prompt);
byte[] audioBytes = response.getResult().getOutput();
TextToSpeechResponseMetadata metadata = response.getMetadata();

编写与提供者无关的代码

共享 TTS 接口的主要优势之一是能够编写无需修改即可与任何 TTS 提供商兼容的代码。实际提供商(OpenAI、ElevenLabs 等)由您的 Spring Boot 配置决定,使您无需更改应用程序代码即可切换提供商。spring-doc.cadn.net.cn

基础服务示例

共享接口允许您编写适用于任何 TTS 提供商的代码:spring-doc.cadn.net.cn

@Service
public class NarrationService {

    private final TextToSpeechModel textToSpeechModel;

    public NarrationService(TextToSpeechModel textToSpeechModel) {
        this.textToSpeechModel = textToSpeechModel;
    }

    public byte[] narrate(String text) {
        // Works with any TTS provider
        return textToSpeechModel.call(text);
    }

    public byte[] narrateWithOptions(String text, TextToSpeechOptions options) {
        TextToSpeechPrompt prompt = new TextToSpeechPrompt(text, options);
        TextToSpeechResponse response = textToSpeechModel.call(prompt);
        return response.getResult().getOutput();
    }
}

该服务与 OpenAI、ElevenLabs 或任何其他 TTS 提供商无缝协作,实际实现由您的 Spring Boot 配置决定。spring-doc.cadn.net.cn

高级示例:多提供商支持

您可以构建同时支持多个 TTS 提供商的应用程序:spring-doc.cadn.net.cn

@Service
public class MultiProviderNarrationService {

    private final Map<String, TextToSpeechModel> providers;

    public MultiProviderNarrationService(List<TextToSpeechModel> models) {
        // Spring will inject all available TextToSpeechModel beans
        this.providers = models.stream()
            .collect(Collectors.toMap(
                model -> model.getClass().getSimpleName(),
                model -> model
            ));
    }

    public byte[] narrateWithProvider(String text, String providerName) {
        TextToSpeechModel model = providers.get(providerName);
        if (model == null) {
            throw new IllegalArgumentException("Unknown provider: " + providerName);
        }
        return model.call(text);
    }

    public Set<String> getAvailableProviders() {
        return providers.keySet();
    }
}

流式音频示例

共享接口还支持实时音频生成的流式处理:spring-doc.cadn.net.cn

@Service
public class StreamingNarrationService {

    private final TextToSpeechModel textToSpeechModel;

    public StreamingNarrationService(TextToSpeechModel textToSpeechModel) {
        this.textToSpeechModel = textToSpeechModel;
    }

    public Flux<byte[]> streamNarration(String text) {
        // TextToSpeechModel extends StreamingTextToSpeechModel
        return textToSpeechModel.stream(text);
    }

    public Flux<TextToSpeechResponse> streamWithMetadata(String text, TextToSpeechOptions options) {
        TextToSpeechPrompt prompt = new TextToSpeechPrompt(text, options);
        return textToSpeechModel.stream(prompt);
    }
}

REST 控制器示例

构建与提供者无关的 TTS 的 REST API:spring-doc.cadn.net.cn

@RestController
@RequestMapping("/api/tts")
public class TextToSpeechController {

    private final TextToSpeechModel textToSpeechModel;

    public TextToSpeechController(TextToSpeechModel textToSpeechModel) {
        this.textToSpeechModel = textToSpeechModel;
    }

    @PostMapping(value = "/synthesize", produces = "audio/mpeg")
    public ResponseEntity<byte[]> synthesize(@RequestBody SynthesisRequest request) {
        byte[] audio = textToSpeechModel.call(request.text());
        return ResponseEntity.ok()
            .contentType(MediaType.parseMediaType("audio/mpeg"))
            .header("Content-Disposition", "attachment; filename=\"speech.mp3\"")
            .body(audio);
    }

    @GetMapping(value = "/stream", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
    public Flux<byte[]> streamSynthesis(@RequestParam String text) {
        return textToSpeechModel.stream(text);
    }

    record SynthesisRequest(String text) {}
}

基于配置的提供程序选择

使用 Spring profiles 或 properties 在不同的提供者之间切换:spring-doc.cadn.net.cn

# application-openai.yml
spring:
  ai:
    model:
      audio:
        speech: openai
    openai:
      api-key: ${OPENAI_API_KEY}
      audio:
        speech:
          options:
            model: gpt-4o-mini-tts
            voice: alloy

# application-elevenlabs.yml
spring:
  ai:
    model:
      audio:
        speech: elevenlabs
    elevenlabs:
      api-key: ${ELEVENLABS_API_KEY}
      tts:
        options:
          model-id: eleven_turbo_v2_5
          voice-id: your_voice_id

然后激活所需的提供者:spring-doc.cadn.net.cn

# Use OpenAI
java -jar app.jar --spring.profiles.active=openai

# Use ElevenLabs
java -jar app.jar --spring.profiles.active=elevenlabs

使用可移植选项

为了获得最大的可移植性,请仅使用通用的 TextToSpeechOptions 接口方法:spring-doc.cadn.net.cn

@Service
public class PortableNarrationService {

    private final TextToSpeechModel textToSpeechModel;

    public PortableNarrationService(TextToSpeechModel textToSpeechModel) {
        this.textToSpeechModel = textToSpeechModel;
    }

    public byte[] createPortableNarration(String text) {
        // Use provider's default options for maximum portability
        TextToSpeechOptions defaultOptions = textToSpeechModel.getDefaultOptions();
        TextToSpeechPrompt prompt = new TextToSpeechPrompt(text, defaultOptions);
        TextToSpeechResponse response = textToSpeechModel.call(prompt);
        return response.getResult().getOutput();
    }
}

使用特定于提供程序的功能

当您需要特定提供者的功能时,您仍然可以在保持可移植代码库的同时使用它们:spring-doc.cadn.net.cn

@Service
public class FlexibleNarrationService {

    private final TextToSpeechModel textToSpeechModel;

    public FlexibleNarrationService(TextToSpeechModel textToSpeechModel) {
        this.textToSpeechModel = textToSpeechModel;
    }

    public byte[] narrate(String text, TextToSpeechOptions baseOptions) {
        TextToSpeechOptions options = baseOptions;

        // Apply provider-specific optimizations if available
        if (textToSpeechModel instanceof OpenAiAudioSpeechModel) {
            options = OpenAiAudioSpeechOptions.builder()
                .from(baseOptions)
                .model("gpt-4o-tts")  // OpenAI-specific: use high-quality model
                .speed(1.0)
                .build();
        } else if (textToSpeechModel instanceof ElevenLabsTextToSpeechModel) {
            // ElevenLabs-specific options could go here
        }

        TextToSpeechPrompt prompt = new TextToSpeechPrompt(text, options);
        TextToSpeechResponse response = textToSpeechModel.call(prompt);
        return response.getResult().getOutput();
    }
}

可移植代码的最佳实践

  1. 依赖接口:始终注入 TextToSpeechModel 而不是具体实现spring-doc.cadn.net.cn

  2. 使用通用选项:坚持使用 TextToSpeechOptions 个接口方法以实现最大的可移植性spring-doc.cadn.net.cn

  3. 优雅地处理元数据:不同的提供者返回不同的元数据;以通用的方式处理它spring-doc.cadn.net.cn

  4. 使用多个提供商进行测试:确保您的代码至少与两个 TTS 提供商兼容spring-doc.cadn.net.cn

  5. 文档提供者假设:如果您依赖特定的提供者行为,请清楚地记录它spring-doc.cadn.net.cn

特定于提供程序的功能

虽然共享接口提供了可移植性,但每个提供者也通过特定于提供者的选项类(例如,OpenAiAudioSpeechOptionsElevenLabsSpeechOptions)提供特定功能。这些类实现了TextToSpeechOptions接口,同时添加了特定于提供者的功能。spring-doc.cadn.net.cn