Documentation
¶
Overview ¶
Package voicecraftali 提供阿里云百炼(Model Studio)语音合成的 Go 客户端, 覆盖三套实时 WebSocket API、三套非实时 HTTP API、声音复刻与声音设计。
实时合成(WebSocket) ¶
阿里云对外暴露三套形态不同的 WebSocket API,本包把它们收敛到同一个 Session 类型上:
| API | 端点 | 模型 | |---------------------------|----------------------|----------------------------------------| | CosyVoice / Qwen-Audio-TTS | /api-ws/v1/inference | qwen-audio-3.0-tts-*、cosyvoice-v1~v3.5 | | Sambert | /api-ws/v1/inference | sambert-*-v1(单向流式) | | Qwen-TTS Realtime | /api-ws/v1/realtime | qwen3-tts-*-realtime、qwen-tts-realtime |
分别通过 Client.NewSynthesisSession、Client.NewSambertSession 与 Client.NewRealtimeSession 创建会话。三者返回的都是 *Session, 操作集合一致,切换 API 不需要改动读写代码。各 API 确实存在的能力差异以 ErrUnsupportedOperation 显式暴露,完整矩阵见 Session 的文档。
会话的写侧动作分终结与非终结两类。非终结动作调用后会话继续可用: SendText 送文本、Commit 提交缓冲区(Realtime)、Flush 强制合成已送入的文本 (CosyVoice / Qwen-Audio-TTS)、CancelResponse 取消当前响应(Realtime)。 终结动作调用后不能再送文本,但仍应读到音频流结束:Finish 正常结束、 Cancel 中断当前任务(CosyVoice / Qwen-Audio-TTS)。
快速开始 ¶
流式合成——边送文本边取音频:
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
return err
}
session, err := client.NewSynthesisSession(ctx, &voicecraftali.SynthesisParams{
Model: voicecraftali.ModelCosyVoiceV3Flash,
Voice: "longanyang",
Format: voicecraftali.FormatMP3,
})
if err != nil {
return err
}
defer session.Close()
go func() {
_ = session.SendText(ctx, "床前明月光,疑是地上霜。")
_ = session.SendText(ctx, "举头望明月,低头思故乡。")
_ = session.Finish(ctx)
}()
err = session.Stream(ctx, func(audio []byte) error {
_, err := file.Write(audio)
return err
})
一次性合成——短文本直接拿到完整音频:
audio, err := client.Synthesize(ctx, params, "床前明月光,疑是地上霜。")
鉴权 ¶
阿里云百炼使用 API Key 鉴权,凭据在 WebSocket 握手阶段校验。 静态密钥用 WithAPIKey;短期密钥、STS 临时凭证或密钥托管轮转的场景用 WithCredentialProvider,本包负责缓存与并发去重,凭据在过期前被复用, 并发请求只触发一次刷新。
两者都不配时,New 回退到 DASHSCOPE_API_KEY 环境变量——这是阿里云百炼文档 统一推荐的变量名。优先级为:显式选项 > 环境变量 > ErrNoCredential。
client, err := voicecraftali.New() // 从 DASHSCOPE_API_KEY 取凭据
北京地域与新加坡地域的 API Key 不通用,需与 WithRegion 匹配。
多 API Key ¶
一个 Client 绑定一套凭据与一套端点配置。进程内要同时使用多个 API Key (多租户、多业务空间分别计费)时用 Registry:每个条目是一个逻辑名加一组 构造选项,因此可以各自配置业务空间、专属域名与自定义基址。
reg, err := voicecraftali.NewRegistry(
[]voicecraftali.Option{voicecraftali.WithUserAgent("svc/1.0")},
voicecraftali.RegistryEntry{
Name: "tenant-a",
Options: []voicecraftali.Option{voicecraftali.WithAPIKey(keyA)},
},
voicecraftali.RegistryEntry{
Name: "tenant-b",
Options: []voicecraftali.Option{
voicecraftali.WithAPIKey(keyB),
voicecraftali.WithWorkspaceID("llm-b"),
voicecraftali.WithDedicatedEndpoint(),
},
},
)
if err != nil {
return err
}
client, err := reg.Client("tenant-b")
注册表条目必须显式配置凭据,不参与 DASHSCOPE_API_KEY 回退:漏配的条目若 静默继承环境里的那一个 key,它的调用会被计入错误的账号。
并发与资源 ¶
Client 构造后不可变,可在多个 goroutine 间共享。
Session 支持「一个 goroutine 写、另一个 goroutine 读」并发使用; 多个 goroutine 并发写会被串行化。Close 幂等,返回后保证后台 goroutine 已退出、连接已释放。详见 Session 的文档。
音频通过有界缓冲交付,写满时接收阻塞形成反压而不丢帧,因此调用方必须 持续读取——长时间不读会把反压传导到服务端并触发其空闲断连。
连接复用 ¶
两套 WebSocket 合成 API 允许在一条连接上按顺序执行多个任务。Pool 把复用 规则(任务结束后换新 task_id、失败过的连接不复用、赶在服务端 60 秒空闲 阈值之前驱逐)封装起来,会话 API 保持不变:
pool := voicecraftali.NewPool(client) defer pool.Close() session, err := pool.NewSynthesisSession(ctx, params)
Qwen-TTS Realtime 的服务端在会话结束后主动关闭连接,用 Client 直接创建即可。
非实时合成(HTTP) ¶
三套 HTTP 接口各有非流式与 SSE 流式两种模式,仅在北京地域可用:
| API | 非流式 | 流式 | |---------------------------|-------------------|------------------| | Qwen-Audio-TTS / CosyVoice | SynthesizeHTTP | StreamHTTP | | Qwen-TTS | SynthesizeQwenTTS | StreamQwenTTS | | MiniMax | SynthesizeMiniMax | StreamMiniMax |
音频交付形态因接口而异,这是服务端的差异:前两套的非流式模式返回有效期 24 小时的下载地址,MiniMax 与所有流式模式直接返回音频字节。 AudioRef.Download 屏蔽了这个差异——已有字节时直接返回,不发请求:
result, err := client.SynthesizeHTTP(ctx, params, "床前明月光")
if err != nil {
return err
}
audio, err := result.Audio.Download(ctx, client)
声音复刻与音色管理 ¶
三套复刻接口的操作集合不同:
| 模型家族 | 创建 | 列表 | 详情 | 更新 | 删除 | |---------------------------|-------------------|----------------|---------|------------|------------------| | Qwen-Audio-TTS / CosyVoice | CreateVoice | ListVoices | GetVoice | UpdateVoice | DeleteVoice | | Qwen-TTS | CreateQwenVoice | ListQwenVoices | — | — | DeleteQwenVoice | | MiniMax | CloneMiniMaxVoice | — | — | — | — |
复刻时指定的 TargetModel 必须与后续合成时使用的模型一致,否则合成会失败。 Qwen-Audio-TTS / CosyVoice 的音色创建后要经过审核, VoiceStatus.Usable 为 true 才能用于合成。
能力与模型的对应关系 ¶
语音合成、声音复刻、声音设计能用的模型不是同一批。target_model 的取值域 复刻是 13 个、设计是 9 个,差集包括:cosyvoice-v2、cosyvoice-v1 与 Qwen-Audio-Realtime 只能复刻;Qwen-TTS 侧复刻只收 qwen3-tts-vc-*、 设计只收 qwen3-tts-vd-*,两组互不通用。
voicecraftali.VoiceCloneTargetModels() // 可作复刻 target_model 的 13 个模型
voicecraftali.VoiceDesignTargetModels() // 可作设计 target_model 的 9 个模型
voicecraftali.MiniMaxModels() // MiniMax 的 4 个模型
if !voicecraftali.SupportsVoiceDesign(model) { /* 该模型不能用于声音设计 */ }
MiniMax 是例外:它支持声音复刻,但走 CloneMiniMaxVoice、用自己的 Model 字段 而不是 target_model,因此不在 VoiceCloneTargetModels 里, SupportsVoiceClone 对它返回 false。
这些清单是官方在册模型的快照,本库不用它们校验参数——TargetModel 只校验非空, 官方新增的模型可以直接传入,不必等本库收录。
声音设计 ¶
用文字描述而不是样本音频生成音色,并返回一段试听音频:
designed, err := client.DesignVoice(ctx, &voicecraftali.DesignVoiceParams{
TargetModel: voicecraftali.ModelCosyVoiceV35Plus,
VoicePrompt: "沉稳的中年男性,音色低沉浑厚",
PreviewText: "各位听众朋友们大家好,欢迎收听本期节目",
Prefix: "announcer",
})
Qwen-Audio-TTS / CosyVoice 侧的设计音色与复刻音色在服务端是同一批, 共用 ListVoices、GetVoice 与 DeleteVoice,用 VoiceSummary.Designed 与 VoiceDetail.Designed 区分来源。Qwen 侧的设计走独立的模型, 因此有独立的 ListDesignedQwenVoices、GetDesignedQwenVoice 与 DeleteDesignedQwenVoice。
SSML 与 LaTeX ¶
两项能力都只覆盖 CosyVoice 系列的五个模型,用 SupportsSSML 判断。
SSML 需要把 EnableSSML 设为 true,且拼接时外部文本要先过 EscapeSSMLText—— SSML 是 XML,未转义的 & 或 < 会让整段标记解析失败。启用 SSML 后整个任务 只允许送一次文本,第二次 SendText 返回 ErrSSMLSingleSend。
LaTeX 公式朗读不需要任何参数:把公式用 $...$ 包起来写进文本即可, 仅支持中文。LaTeX 文本不要过 EscapeSSMLText——反斜杠不是 XML 特殊字符。
错误处理 ¶
错误分五类具名类型:ValidationError(本地参数校验)、TaskFailedError (两套 WebSocket 合成 API 的 task-failed)、EventError(Qwen-TTS Realtime 的 error 事件)、HandshakeError(WebSocket 握手失败)、HTTPError (非实时 HTTP 接口失败),均支持 errors.Is 与 errors.As。
服务端返回码收录于 codes.go,CodeDescription 给出中文说明, Retryable 判断该错误是否值得重试:
if err != nil {
if code := voicecraftali.ErrorCode(err); code != "" {
log.Printf("合成失败 %s: %s", code, voicecraftali.CodeDescription(code))
}
if voicecraftali.Retryable(err) {
// 限流、服务端内部错误、模型不可用、超时——退避后重试
}
}
重试策略(退避、抖动、次数上限)由调用方决定,本包只提供判定。
Index ¶
- Constants
- Variables
- func CodeDescription(code Code) string
- func DataURLMIME(dataURL string) string
- func EscapeSSMLText(s string) string
- func MiniMaxModels() []string
- func Retryable(err error) bool
- func SupportsSSML(model string) bool
- func SupportsVoiceClone(model string) bool
- func SupportsVoiceDesign(model string) bool
- func VoiceCloneTargetModels() []string
- func VoiceDesignTargetModels() []string
- func VoiceStatusDescription(s VoiceStatus) string
- type AudioFormat
- type AudioRef
- type Client
- func (c *Client) CloneMiniMaxVoice(ctx context.Context, params *MiniMaxCloneParams) (*MiniMaxCloneResult, error)
- func (c *Client) CreateQwenVoice(ctx context.Context, params *CreateQwenVoiceParams) (*QwenVoice, error)
- func (c *Client) CreateVoice(ctx context.Context, params *CreateVoiceParams) (string, error)
- func (c *Client) DeleteDesignedQwenVoice(ctx context.Context, voice string) error
- func (c *Client) DeleteQwenVoice(ctx context.Context, voice string) error
- func (c *Client) DeleteVoice(ctx context.Context, voiceID string) error
- func (c *Client) DesignQwenVoice(ctx context.Context, params *DesignQwenVoiceParams) (*DesignedQwenVoice, error)
- func (c *Client) DesignVoice(ctx context.Context, params *DesignVoiceParams) (*DesignedVoice, error)
- func (c *Client) GetDesignedQwenVoice(ctx context.Context, voice string) (*DesignedQwenVoiceDetail, error)
- func (c *Client) GetVoice(ctx context.Context, voiceID string) (*VoiceDetail, error)
- func (c *Client) ListDesignedQwenVoices(ctx context.Context, params *ListVoicesParams) (*DesignedQwenVoiceList, error)
- func (c *Client) ListQwenVoices(ctx context.Context, params *ListVoicesParams) (*QwenVoiceList, error)
- func (c *Client) ListVoices(ctx context.Context, params *ListVoicesParams) ([]VoiceSummary, error)
- func (c *Client) NewRealtimeSession(ctx context.Context, params *RealtimeParams, opts ...SessionOption) (*Session, error)
- func (c *Client) NewSambertSession(ctx context.Context, params *SambertParams, opts ...SessionOption) (*Session, error)
- func (c *Client) NewSynthesisSession(ctx context.Context, params *SynthesisParams, opts ...SessionOption) (*Session, error)
- func (c *Client) StreamHTTP(ctx context.Context, params *HTTPSynthesisParams, text string, ...) (*SynthesisResult, error)
- func (c *Client) StreamMiniMax(ctx context.Context, params *MiniMaxParams, text string, ...) (*MiniMaxResult, error)
- func (c *Client) StreamQwenTTS(ctx context.Context, params *QwenTTSParams, text string, ...) (*SynthesisResult, error)
- func (c *Client) Synthesize(ctx context.Context, params *SynthesisParams, text string) ([]byte, error)
- func (c *Client) SynthesizeHTTP(ctx context.Context, params *HTTPSynthesisParams, text string) (*SynthesisResult, error)
- func (c *Client) SynthesizeMiniMax(ctx context.Context, params *MiniMaxParams, text string) (*MiniMaxResult, error)
- func (c *Client) SynthesizeQwenTTS(ctx context.Context, params *QwenTTSParams, text string) (*SynthesisResult, error)
- func (c *Client) SynthesizeRealtime(ctx context.Context, params *RealtimeParams, text string) ([]byte, error)
- func (c *Client) SynthesizeSambert(ctx context.Context, params *SambertParams, text string) ([]byte, error)
- func (c *Client) UpdateVoice(ctx context.Context, voiceID, audioURL string) error
- type Code
- type CreateQwenVoiceParams
- type CreateVoiceParams
- type Credential
- type CredentialProvider
- type DesignQwenVoiceParams
- type DesignVoiceParams
- type DesignedQwenVoice
- type DesignedQwenVoiceDetail
- type DesignedQwenVoiceList
- type DesignedQwenVoiceSummary
- type DesignedVoice
- type EventError
- type HTTPError
- type HTTPSynthesisParams
- type HandshakeError
- type HotFix
- type ListVoicesParams
- type MiniMaxAudioSetting
- type MiniMaxCloneParams
- type MiniMaxClonePrompt
- type MiniMaxCloneResult
- type MiniMaxExtraInfo
- type MiniMaxParams
- type MiniMaxResult
- type MiniMaxTimbreWeight
- type MiniMaxVoiceSetting
- type Option
- func WithAPIKey(key string) Option
- func WithAudioBuffer(frames int) Option
- func WithBaseURL(raw string) Option
- func WithCredentialProvider(p CredentialProvider) Option
- func WithCredentialRefresh(skew, timeout time.Duration) Option
- func WithDataInspection() Option
- func WithDedicatedEndpoint() Option
- func WithHTTPClient(hc *http.Client) Option
- func WithHandshakeTimeout(d time.Duration) Option
- func WithReadIdleTimeout(d time.Duration) Option
- func WithRegion(r Region) Option
- func WithUserAgent(ua string) Option
- func WithWorkspaceID(id string) Option
- func WithWriteTimeout(d time.Duration) Option
- type Phoneme
- type Pool
- type PoolOption
- type PreviewAudio
- type PreviewOptions
- type Progress
- type Protocol
- type QwenTTSParams
- type QwenVoice
- type QwenVoiceAudio
- type QwenVoiceList
- type QwenVoiceSummary
- type RealtimeMode
- type RealtimeParams
- type Region
- type Registry
- type RegistryEntry
- type SambertParams
- type Sentence
- type Session
- func (s *Session) Cancel(ctx context.Context) error
- func (s *Session) CancelResponse(ctx context.Context) error
- func (s *Session) Close() error
- func (s *Session) Commit(ctx context.Context) error
- func (s *Session) Finish(ctx context.Context) error
- func (s *Session) Flush(ctx context.Context) error
- func (s *Session) ID() string
- func (s *Session) Protocol() Protocol
- func (s *Session) Read() ([]byte, error)
- func (s *Session) ReadContext(ctx context.Context) ([]byte, error)
- func (s *Session) SendText(ctx context.Context, text string) error
- func (s *Session) Stream(ctx context.Context, handler func(audio []byte) error) error
- func (s *Session) Usage() *Usage
- type SessionOption
- type SynthesisParams
- type SynthesisResult
- type TaskFailedError
- type Usage
- type ValidationError
- type VoiceDetail
- type VoiceStatus
- type VoiceSummary
- type Word
Examples ¶
- Client.CloneMiniMaxVoice
- Client.CreateQwenVoice
- Client.CreateVoice
- Client.DesignQwenVoice
- Client.DesignVoice
- Client.ListVoices
- Client.NewRealtimeSession
- Client.NewSambertSession
- Client.NewSynthesisSession
- Client.StreamHTTP
- Client.Synthesize
- Client.Synthesize (LaTeX)
- Client.SynthesizeHTTP
- Client.SynthesizeMiniMax
- Client.SynthesizeQwenTTS
- EscapeSSMLText
- New
- New (FromEnvironment)
- New (MissingCredential)
- NewPool
- NewRegistry
- Registry.Client
- Retryable
- Session.CancelResponse
- Session.Flush
- WithCredentialProvider
- WithProgress
Constants ¶
const ( LanguageAuto = "Auto" LanguageChinese = "Chinese" LanguageEnglish = "English" LanguageGerman = "German" LanguageItalian = "Italian" LanguagePortuguese = "Portuguese" LanguageSpanish = "Spanish" LanguageJapanese = "Japanese" LanguageKorean = "Korean" LanguageFrench = "French" LanguageRussian = "Russian" )
Qwen-TTS Realtime 支持的 language_type 取值。
const ( // ModelMiniMaxSpeech28HD MiniMax/speech-2.8-hd,效果优先。 ModelMiniMaxSpeech28HD = "MiniMax/speech-2.8-hd" // ModelMiniMaxSpeech02HD MiniMax/speech-02-hd。 ModelMiniMaxSpeech02HD = "MiniMax/speech-02-hd" // ModelMiniMaxSpeech28Turbo MiniMax/speech-2.8-turbo,延迟优先。 ModelMiniMaxSpeech28Turbo = "MiniMax/speech-2.8-turbo" // ModelMiniMaxSpeech02Turbo MiniMax/speech-02-turbo。 ModelMiniMaxSpeech02Turbo = "MiniMax/speech-02-turbo" )
MiniMax 非实时 HTTP 合成的模型。
const ( EmotionHappy = "happy" // 高兴 EmotionSad = "sad" // 悲伤 EmotionAngry = "angry" // 愤怒 EmotionFearful = "fearful" // 害怕 EmotionDisgusted = "disgusted" // 厌恶 EmotionSurprised = "surprised" // 惊讶 EmotionCalm = "calm" // 中性 // EmotionWhisper 低语。speech-2.8-hd 与 speech-2.8-turbo 不支持。 EmotionWhisper = "whisper" )
MiniMax 支持的情感。
const ( // ModelQwen3TTSFlash qwen3-tts-flash 稳定版。 ModelQwen3TTSFlash = "qwen3-tts-flash" // ModelQwen3TTSInstructFlash qwen3-tts-instruct-flash 稳定版,支持指令控制。 ModelQwen3TTSInstructFlash = "qwen3-tts-instruct-flash" // ModelQwen3TTSVC qwen3-tts-vc 快照版,用于声音复刻音色。 ModelQwen3TTSVC = "qwen3-tts-vc-2026-01-22" // ModelQwen3TTSVD qwen3-tts-vd 快照版,用于声音设计音色。 ModelQwen3TTSVD = "qwen3-tts-vd-2026-01-26" // ModelQwenTTS qwen-tts 稳定版(旧版,按 Token 计费)。 ModelQwenTTS = "qwen-tts" // ModelQwenTTSLatest qwen-tts-latest(旧版,按 Token 计费)。 ModelQwenTTSLatest = "qwen-tts-latest" )
Qwen-TTS 非实时 HTTP 合成的模型。
与 WebSocket 侧按模型名后缀区分:带 -realtime 的走 WebSocket, 不带的走本 HTTP 接口。
const ( // ModelQwenAudioTTSPlus qwen-audio-3.0-tts-plus,效果优先。 ModelQwenAudioTTSPlus = "qwen-audio-3.0-tts-plus" // ModelQwenAudioTTSFlash qwen-audio-3.0-tts-flash,延迟优先。 ModelQwenAudioTTSFlash = "qwen-audio-3.0-tts-flash" // ModelCosyVoiceV35Plus cosyvoice-v3.5-plus(仅北京地域)。 ModelCosyVoiceV35Plus = "cosyvoice-v3.5-plus" // ModelCosyVoiceV35Flash cosyvoice-v3.5-flash(仅北京地域)。 ModelCosyVoiceV35Flash = "cosyvoice-v3.5-flash" // ModelCosyVoiceV3Plus cosyvoice-v3-plus。 ModelCosyVoiceV3Plus = "cosyvoice-v3-plus" // ModelCosyVoiceV3Flash cosyvoice-v3-flash。 ModelCosyVoiceV3Flash = "cosyvoice-v3-flash" // ModelCosyVoiceV2 cosyvoice-v2(仅北京地域)。 ModelCosyVoiceV2 = "cosyvoice-v2" // ModelCosyVoiceV1 cosyvoice-v1(仅北京地域)。不支持 opus 格式、bit_rate 与 seed 参数。 ModelCosyVoiceV1 = "cosyvoice-v1" )
CosyVoice / Qwen-Audio-TTS(WebSocket 双向流式)的模型。
这两个系列可在会话中多次 SendText 增量送入文本。
const ( // ModelQwenAudioRealtimePlus qwen-audio-3.0-realtime-plus(仅北京地域)。 ModelQwenAudioRealtimePlus = "qwen-audio-3.0-realtime-plus" // ModelQwenAudioRealtimeFlash qwen-audio-3.0-realtime-flash(仅北京地域)。 ModelQwenAudioRealtimeFlash = "qwen-audio-3.0-realtime-flash" )
Qwen-Audio-Realtime 模型。
本库不覆盖该系列的合成 API——它是实时语音对话模型,不是 TTS 接口。 收录这两个常量只为一个用途:它们是官方在册的声音复刻 target_model, 可填入 CreateVoiceParams.TargetModel 把音色绑定到该系列。 绑定后的合成需自行调用对话 API,本库不提供。
const ( // ModelQwen3TTSInstructFlashRealtime qwen3-tts-instruct-flash-realtime 稳定版, // 支持 Instructions 指令控制。 ModelQwen3TTSInstructFlashRealtime = "qwen3-tts-instruct-flash-realtime" // ModelQwen3TTSFlashRealtime qwen3-tts-flash-realtime 稳定版。 ModelQwen3TTSFlashRealtime = "qwen3-tts-flash-realtime" // ModelQwen3TTSVCRealtime qwen3-tts-vc-realtime 最新快照版,用于声音复刻(Qwen)音色。 ModelQwen3TTSVCRealtime = "qwen3-tts-vc-realtime-2026-01-15" // ModelQwen3TTSVCRealtimePrev qwen3-tts-vc-realtime 上一个快照版。 // 官方仍在册,供已绑定该版本的存量音色继续使用;新音色用 // ModelQwen3TTSVCRealtime。 ModelQwen3TTSVCRealtimePrev = "qwen3-tts-vc-realtime-2025-11-27" // ModelQwen3TTSVDRealtime qwen3-tts-vd-realtime 最新快照版,用于声音设计(Qwen)音色。 ModelQwen3TTSVDRealtime = "qwen3-tts-vd-realtime-2026-01-15" // ModelQwen3TTSVDRealtimePrev qwen3-tts-vd-realtime 上一个快照版。 // 官方仍在册,供已绑定该版本的存量音色继续使用;新音色用 // ModelQwen3TTSVDRealtime。 ModelQwen3TTSVDRealtimePrev = "qwen3-tts-vd-realtime-2025-12-16" // ModelQwenTTSRealtime qwen-tts-realtime 稳定版。 // 该系列只支持 pcm 格式与 24000 采样率,且不支持语速、音量、语调与码率参数。 ModelQwenTTSRealtime = "qwen-tts-realtime" )
Qwen-TTS Realtime(WebSocket 事件流)的模型。
const ( // ModelSambertZhichu sambert-zhichu-v1。 ModelSambertZhichu = "sambert-zhichu-v1" // ModelSambertZhinan sambert-zhinan-v1。 ModelSambertZhinan = "sambert-zhinan-v1" // ModelSambertZhiqi sambert-zhiqi-v1。 ModelSambertZhiqi = "sambert-zhiqi-v1" // ModelSambertZhide sambert-zhide-v1。 ModelSambertZhide = "sambert-zhide-v1" // ModelSambertZhijia sambert-zhijia-v1。 ModelSambertZhijia = "sambert-zhijia-v1" // ModelSambertZhiru sambert-zhiru-v1。 ModelSambertZhiru = "sambert-zhiru-v1" )
Sambert 模型(WebSocket 单向流式)。
Sambert 是早期语音合成模型,走单向流式:全文在建立会话时一次性提交, 不支持增量 SendText。新项目建议优先选用 CosyVoice 或 Qwen-Audio-TTS。 完整模型列表见官方 Sambert 文档。
const ( VoiceLongAnLingXin = "longanlingxin" // 龙安灵心,知心温暖音,女 VoiceLongAnLuFeng = "longanlufeng" // 龙安鲁风,明亮开朗音,男 VoiceLongAnFengYue = "longanfengyue" // 龙安风悦,自然亲切音,女 VoiceLongAnYuanFei = "longanyuanfei" // 龙安元妃,高傲妃子音,女 VoiceLongAnLingXi = "longanlingxi" // 龙安灵希,可爱甜美音,女 VoiceLongAnXiaoXin = "longanxiaoxin" // 龙安小昕,亲切活泼音,女 VoiceLongAnHuan = "longanhuan_v3.6" // 龙安欢,女 VoiceLongJieLiDou = "longjielidou_v3.6" VoiceLongPaoPao = "longpaopao_v3.6" VoiceLongHuoHuo = "longhuohuo_v3.6" VoiceLongChuanShu = "longchuanshu_v3.6" VoiceLoongMary = "loongmary" VoiceLoongEva = "loongeva_v3.6" VoiceLoongJohn = "loongjohn" )
Qwen-Audio-TTS 系统音色(配合 ModelQwenAudioTTSPlus / ModelQwenAudioTTSFlash)。
const ( VoiceCherry = "Cherry" // 芊悦,阳光积极、亲切自然,女 VoiceSerena = "Serena" // 苏瑶,温柔,女 VoiceEthan = "Ethan" // 晨煦,阳光温暖有活力,男 VoiceChelsie = "Chelsie" // 千雪,二次元虚拟女友,女 VoiceMomo = "Momo" // 茉兔,撒娇搞怪,女 VoiceVivian = "Vivian" // 十三,拽拽的小暴躁,女 VoiceMoon = "Moon" // 月白,率性帅气,男 VoiceMaia = "Maia" // 四月,知性温柔,女 VoiceKai = "Kai" // 凯,男 VoiceNofish = "Nofish" // 不吃鱼,不会翘舌音的设计师,男 VoiceBella = "Bella" // 萌宝,小萝莉,女 VoiceJennifer = "Jennifer" // 詹妮弗,电影质感美语女声 VoiceRyan = "Ryan" // 甜茶,戏感张力,男 VoiceKaterina = "Katerina" // 卡捷琳娜,御姐音色,女 VoiceAiden = "Aiden" // 艾登,美语大男孩 VoiceEldricSage = "Eldric Sage" // 沧明子,沉稳睿智的老者,男 VoiceMia = "Mia" // 乖小妹,女 VoiceMochi = "Mochi" // 沙小弥,聪明伶俐的小大人,男 VoiceBellona = "Bellona" // 燕铮莺,声音洪亮吐字清晰,女 VoiceVincent = "Vincent" // 田叔,沙哑烟嗓,男 VoiceBunny = "Bunny" // 萌小姬,小萝莉,女 VoiceNeil = "Neil" // 阿闻,新闻主持人,男 VoiceElias = "Elias" // 墨讲师,学科严谨,女 VoiceArthur = "Arthur" // 徐大爷,质朴嗓音,男 VoiceNini = "Nini" // 邻家妹妹 VoiceSeren = "Seren" // 小婉 VoicePip = "Pip" // 顽屁小孩 VoiceStella = "Stella" // 少女阿月 VoiceBodega = "Bodega" // 博德加 VoiceSonrisa = "Sonrisa" // 索尼莎 VoiceAlek = "Alek" // 阿列克 VoiceDolce = "Dolce" // 多尔切 VoiceSohee = "Sohee" // 素熙 VoiceOnoAnna = "Ono Anna" // 小野杏 VoiceLenn = "Lenn" // 莱恩 VoiceEmilien = "Emilien" // 埃米尔安 VoiceAndre = "Andre" // 安德雷 VoiceRadioGol = "Radio Gol" // 拉迪奥·戈尔 )
Qwen-TTS Realtime 系统音色(配合 ModelQwen3TTS*Realtime / ModelQwenTTSRealtime)。
各音色支持的具体模型版本不同,详见官方 Qwen-TTS 音色列表文档。
const ( VoiceJada = "Jada" // 上海-阿珍 VoiceDylan = "Dylan" // 北京-晓东 VoiceLi = "Li" // 南京-老李 VoiceMarcus = "Marcus" // 陕西-秦川 VoiceRoy = "Roy" // 闽南-阿杰 VoicePeter = "Peter" // 天津-李彼得 VoiceSunny = "Sunny" // 四川-晴儿 VoiceEric = "Eric" // 四川-程川 VoiceRocky = "Rocky" // 粤语-阿强 VoiceKiki = "Kiki" // 粤语-阿清 )
Qwen-TTS Realtime 方言音色。
const ( // SensitiveNone 正常,未命中风控。 SensitiveNone = 0 // SensitiveSevere 严重违规。 SensitiveSevere = 1 // SensitivePorn 色情。 SensitivePorn = 2 )
MiniMax 输入音频命中风控的类型。
const Version = "v1.2.0"
Version 是本包的当前版本号,与 git 附注标签保持一致。
发版走 make release-patch / make release-minor:脚本会原地自增下面这行的版本号、 提交并据此打标签,因此这一行的形状——Version 常量赋值为带 v 前缀的三段版本号—— 不能改动,本注释内也不得出现版本号字面量(脚本取文件里第一个匹配到的版本号)。
Variables ¶
var ( // ErrNoCredential 表示构造时找不到可用凭据。 // // New 的凭据来源优先级为:WithAPIKey 或 WithCredentialProvider 显式配置 > // DASHSCOPE_API_KEY 环境变量 > 本错误。NewRegistry 的条目不参与环境变量 // 回退,未显式配置凭据即返回本错误。 ErrNoCredential = errors.New("voicecraftali: no credential configured") // ErrInvalidCredential 表示凭据提供者返回了空凭据。空凭据无法用于握手, // 因此不会被缓存,也不会被送往服务端。 ErrInvalidCredential = errors.New("voicecraftali: credential provider returned an empty credential") // ErrUnknownClient 表示 Registry 中不存在该名字的条目。 // // 独立成一个哨兵错误是为了让多租户调用方能把「未知条目名」与配置错误分开 // 处理——前者通常对应一个客户端错误,后者对应服务端配置问题。 ErrUnknownClient = errors.New("voicecraftali: unknown client name") // ErrInvalidParam 表示参数校验失败。所有 *ValidationError 都能被 // errors.Is(err, ErrInvalidParam) 命中,便于调用方在不关心具体字段时统一分支。 ErrInvalidParam = errors.New("voicecraftali: invalid parameter") // ErrSessionClosed 表示会话已关闭,不能再执行写操作。 ErrSessionClosed = errors.New("voicecraftali: session closed") // ErrSessionFinished 表示会话已调用 Finish,不能再送入文本。 // 已 Finish 的会话仍可继续读取服务端返回的剩余音频。 ErrSessionFinished = errors.New("voicecraftali: session already finished") // ErrTextTooLong 表示待合成文本超过协议允许的单次长度上限。 ErrTextTooLong = errors.New("voicecraftali: text exceeds length limit") // ErrUnsupportedOperation 表示当前协议或模型不支持该操作, // 例如在 WebSocket 合成 API 上调用 Commit、在 Sambert 或 // Qwen-TTS Realtime 上调用 Cancel、在 Sambert 上调用 SendText。 ErrUnsupportedOperation = errors.New("voicecraftali: operation not supported by this protocol") // ErrPoolClosed 表示连接池已关闭,不能再从中创建会话。 ErrPoolClosed = errors.New("voicecraftali: pool closed") // ErrSSMLSingleSend 表示会话启用了 SSML,而 SSML 模式下整个任务 // 只允许送入一次文本。 ErrSSMLSingleSend = errors.New("voicecraftali: SSML mode allows only one text submission") )
哨兵错误。调用方用 errors.Is 判断错误类别。
var ErrStreamTruncated = errors.New("voicecraftali: sse stream ended before the completion marker")
ErrStreamTruncated 表示上游 SSE 流在给出结束标记之前就结束了。
这通常是中间代理超时或服务端异常收尾造成的。此时已收到的音频是残缺的, 本库把它作为错误上报而不是当成正常结束——静默返回半截音频会被调用方 当成完整结果写进文件,比直接失败更难排查。
Functions ¶
func CodeDescription ¶
CodeDescription 返回返回码的中文说明。
未收录的码(含空字符串)返回空字符串——阿里云会随文档更新新增返回码, 让未知码表现为「没有说明」而不是 panic 或错误的说明。
func DataURLMIME ¶
DataURLMIME 从 Data URL 中取出 MIME 类型,用于校验或排查。 不是 Data URL 时返回空字符串。
func EscapeSSMLText ¶
EscapeSSMLText 转义一段要嵌进 SSML 的普通文本。
SSML 是 XML:文本里未转义的 &、<、> 会让整段标记解析失败,服务端返回 参数错误而不是把它们读出来。拼接 SSML 时,凡是来自外部的文本都应先过一遍 本函数:
text := "Q&A 环节 <请注意>" ssml := "<speak>" + voicecraftali.EscapeSSMLText(text) + "</speak>" // <speak>Q&A 环节 <请注意></speak>
只转义文本内容,不校验 SSML 结构——标签的正确性由调用方保证。
LaTeX 公式不要用本函数处理:反斜杠不是 XML 特殊字符,无需转义; 需要留意的是 Go 字符串字面量里 \ 要写成 \\(如 LaTeX 的 \frac 写作 "\\frac"), 或者直接用反引号原样字符串。
Example ¶
ExampleEscapeSSMLText 展示用 SSML 精细控制发音。
SSML 是 XML:来自外部的文本必须先转义,否则 & 或 < 会让整段标记解析失败。
package main
import (
"context"
"log"
"os"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
userInput := `Q&A 环节 <请注意>` // 来自外部,含 XML 特殊字符
ssml := "<speak>" +
voicecraftali.EscapeSSMLText(userInput) +
`<break time="500ms"/>` +
`<sub alias="人工智能">AI</sub>` +
`<say-as interpret-as="telephone">10086</say-as>` +
"</speak>"
// SSML 仅 CosyVoice 系列的部分模型支持,可先用 SupportsSSML 判断。
model := voicecraftali.ModelCosyVoiceV3Flash
if !voicecraftali.SupportsSSML(model) {
log.Fatalf("模型 %s 不支持 SSML", model)
}
audio, err := client.Synthesize(context.Background(), &voicecraftali.SynthesisParams{
Model: model,
Voice: "longanyang",
EnableSSML: true, // 启用后整个任务只能送一次文本
}, ssml)
if err != nil {
log.Fatal(err)
}
_ = audio
}
Output:
func MiniMaxModels ¶ added in v1.1.0
func MiniMaxModels() []string
MiniMaxModels 返回 MiniMax 系列的全部模型名。
这些取值用于 MiniMaxParams.Model(合成)与 MiniMaxCloneParams.Model(声音复刻), **不用于任何 TargetModel 字段**——MiniMax 的复刻不经过 target_model。
与这两处参数校验用的是同一份清单,因此本函数返回的每个取值都能通过校验。
返回的是副本,调用方修改它不会影响本库。
func Retryable ¶
Retryable 判断错误是否值得重试。
判定为可重试的情形:
- 服务端返回限流、内部错误、模型不可用、超时类返回码
- 握手失败且 HTTP 状态码为 429 或 5xx
- SSE 流在结束标记之前断开(ErrStreamTruncated)
判定为不可重试的情形:参数错误、鉴权错误、欠费、资源不存在, 以及 nil、未收录返回码与本库之外的错误。
本库不内置重试循环:退避策略、抖动与次数上限属于调用方的策略选择, 可与 github.com/gtkit/tools/retry 之类的组件配合:
for attempt := range 3 {
audio, err := client.Synthesize(ctx, params, text)
if err == nil || !voicecraftali.Retryable(err) {
return audio, err
}
time.Sleep(backoff(attempt))
}
Example ¶
ExampleRetryable 展示按错误码决定是否重试。 退避策略由调用方决定,本库只提供判定。
package main
import (
"context"
"log"
"os"
"time"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
params := &voicecraftali.SynthesisParams{
Model: voicecraftali.ModelCosyVoiceV3Flash,
Voice: "longanyang",
}
var audio []byte
for attempt := range 3 {
audio, err = client.Synthesize(context.Background(), params, "床前明月光")
if err == nil {
break
}
if code := voicecraftali.ErrorCode(err); code != "" {
log.Printf("合成失败 %s: %s", code, voicecraftali.CodeDescription(code))
}
if !voicecraftali.Retryable(err) {
log.Fatal(err) // 参数错误、鉴权失败——重试也没用
}
time.Sleep(time.Duration(1<<attempt) * time.Second)
}
_ = audio
}
Output:
func SupportsSSML ¶
SupportsSSML 判断模型是否支持 SSML 标记语言与 LaTeX 公式朗读。
除模型外还有音色维度的限制:仅复刻音色以及官方音色列表中标注为支持 SSML 的系统音色可用。音色维度无法在本地判定,用不支持的音色时服务端会返回错误。
func SupportsVoiceClone ¶ added in v1.1.0
SupportsVoiceClone 判断 model 是否在声音复刻的 target_model 清单内。
回答的仅仅是「该模型是否在官方 target_model 列表内」,不含地域可用性与 账号开通状态,也不含 MiniMax——对 MiniMax 模型名它返回 false, 因为 MiniMax 的复刻不通过 target_model,那条路径见 MiniMaxModels。
返回 false 不代表服务端一定拒绝:清单是快照,官方新增的模型本库尚未收录时 同样返回 false。本库自身不用它拒绝任何请求,调用方要不要据此前置拦截,自行决定。
func SupportsVoiceDesign ¶ added in v1.1.0
SupportsVoiceDesign 判断 model 是否在声音设计的 target_model 清单内。
回答的仅仅是「该模型是否在官方 target_model 列表内」,不含地域可用性与 账号开通状态。对 cosyvoice-v2、cosyvoice-v1 与全部 MiniMax 模型返回 false—— 它们不支持声音设计。
返回 false 不代表服务端一定拒绝:清单是快照,官方新增的模型本库尚未收录时 同样返回 false。本库自身不用它拒绝任何请求,调用方要不要据此前置拦截,自行决定。
func VoiceCloneTargetModels ¶ added in v1.1.0
func VoiceCloneTargetModels() []string
VoiceCloneTargetModels 返回全部可作声音复刻 target_model 的模型名。
这些取值可填入 CreateVoiceParams.TargetModel(Qwen-Audio-TTS / CosyVoice) 与 CreateQwenVoiceParams.TargetModel(Qwen-TTS,只接受其中的 qwen3-tts-vc-* 三个)。
清单**不含 MiniMax**:MiniMax 支持声音复刻,但走 CloneMiniMaxVoice, 参数是自己的 Model 字段而不是 target_model,其取值见 MiniMaxModels。
返回的是副本,调用方修改它不会影响本库。
清单是截至 2026-09-03 官方在册模型的快照,仅供参考,本库不用它校验参数: 未收录的模型可以直接传入,是否接受由服务端决定。地域可用性见各模型常量的注释。
func VoiceDesignTargetModels ¶ added in v1.1.0
func VoiceDesignTargetModels() []string
VoiceDesignTargetModels 返回全部可作声音设计 target_model 的模型名。
这些取值可填入 DesignVoiceParams.TargetModel(Qwen-Audio-TTS / CosyVoice) 与 DesignQwenVoiceParams.TargetModel(Qwen-TTS,只接受其中的 qwen3-tts-vd-* 三个)。
比声音复刻的清单短:cosyvoice-v2、cosyvoice-v1 与 Qwen-Audio-Realtime 只能复刻, MiniMax 则完全不支持声音设计。
返回的是副本,调用方修改它不会影响本库。
清单是截至 2026-09-03 官方在册模型的快照,仅供参考,本库不用它校验参数: 未收录的模型可以直接传入,是否接受由服务端决定。地域可用性见各模型常量的注释。
func VoiceStatusDescription ¶
func VoiceStatusDescription(s VoiceStatus) string
VoiceStatusDescription 返回音色状态的中文说明。 未收录的状态返回空字符串。
Types ¶
type AudioFormat ¶
type AudioFormat string
AudioFormat 是输出音频的编码格式。
const ( // FormatPCM 裸 PCM。 FormatPCM AudioFormat = "pcm" // FormatWAV WAV 容器。 FormatWAV AudioFormat = "wav" // FormatMP3 MP3。 FormatMP3 AudioFormat = "mp3" // FormatOpus Opus。cosyvoice-v1 与 Sambert 不支持该格式。 FormatOpus AudioFormat = "opus" // FormatFLAC FLAC。仅 MiniMax 支持该格式。 FormatFLAC AudioFormat = "flac" )
type AudioRef ¶
type AudioRef struct {
// Data 是音频字节。流式模式与 MiniMax 非流式模式下有值。
Data []byte
// URL 是音频下载地址,有效期 24 小时。
// Qwen-Audio-TTS / CosyVoice 与 Qwen-TTS 的非流式模式下有值。
URL string
// ID 是服务端为该音频分配的标识。
ID string
// ExpiresAt 是 URL 的过期时刻,URL 为空时为零值。
ExpiresAt time.Time
}
AudioRef 是一段可下载的音频。
Qwen-Audio-TTS / CosyVoice 与 Qwen-TTS 的非流式 HTTP 合成不直接返回音频字节, 而是返回一个有效期 24 小时的下载地址;MiniMax 与所有 API 的流式模式则直接 返回音频字节。两种形态在本类型里共存,用哪一个取决于所调用的接口, 见各方法的文档。
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client 是阿里云百炼实时语音合成的客户端。
Client 在 New 返回后不可变,且可以安全地在多个 goroutine 间共享 (safe for concurrent use)。每次创建会话都会新建一条 WebSocket 连接, 会话之间不共享可变状态。
用法:
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
return err
}
session, err := client.NewSynthesisSession(ctx, &voicecraftali.SynthesisParams{
Model: voicecraftali.ModelCosyVoiceV3Flash,
Voice: "longanyang",
})
func New ¶
New 构造一个 Client。
凭据来源的优先级:WithAPIKey 或 WithCredentialProvider 显式配置 > DASHSCOPE_API_KEY 环境变量 > ErrNoCredential。环境变量未设置或为空字符串 一律视为未配置——空凭据握手必然失败,没有理由让它通过构造期。
其余配置均有默认值,只配鉴权即可使用。
Example ¶
ExampleNew 展示最小构造。
package main
import (
"fmt"
"log"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(
voicecraftali.WithAPIKey("sk-xxx"),
voicecraftali.WithRegion(voicecraftali.RegionBeijing),
)
if err != nil {
log.Fatal(err)
}
fmt.Println(client != nil)
}
Output: true
Example (FromEnvironment) ¶
ExampleNew_fromEnvironment 展示从 DASHSCOPE_API_KEY 取凭据。
显式的 WithAPIKey 与 WithCredentialProvider 都优先于环境变量。
package main
import (
"fmt"
"log"
"os"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
// 真实使用中这个变量由部署环境提供,这里为了让示例输出确定而就地设置。
os.Setenv("DASHSCOPE_API_KEY", "sk-from-env")
defer os.Unsetenv("DASHSCOPE_API_KEY")
client, err := voicecraftali.New()
if err != nil {
log.Fatal(err)
}
fmt.Println(client != nil)
}
Output: true
Example (MissingCredential) ¶
ExampleNew_missingCredential 展示既没有显式凭据、DASHSCOPE_API_KEY 也没有值时的行为。
package main
import (
"errors"
"fmt"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
_, err := voicecraftali.New()
fmt.Println(errors.Is(err, voicecraftali.ErrNoCredential))
}
Output: true
func (*Client) CloneMiniMaxVoice ¶
func (c *Client) CloneMiniMaxVoice( ctx context.Context, params *MiniMaxCloneParams, ) (*MiniMaxCloneResult, error)
CloneMiniMaxVoice 用 MiniMax 复刻一个音色。
与阿里云自研的复刻接口不同:音色 ID 由调用方自定义且全局唯一, 必须提供试听文本,且**试听音频会按所选模型的合成单价额外计费**。
MiniMax 不提供音色列表、详情、更新与删除接口;已复刻音色的查询需前往 控制台的声音管理页面。
result, err := client.CloneMiniMaxVoice(ctx, &voicecraftali.MiniMaxCloneParams{
Model: voicecraftali.ModelMiniMaxSpeech28Turbo,
VoiceID: "myvoice20260824",
AudioURL: "https://example.com/sample.wav",
Text: "今天天气怎么样?",
})
Example ¶
ExampleClient_CloneMiniMaxVoice 展示 MiniMax 的声音复刻。
音色 ID 由调用方自定义且全局唯一;试听音频会按合成价额外计费。
package main
import (
"context"
"log"
"os"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
result, err := client.CloneMiniMaxVoice(context.Background(), &voicecraftali.MiniMaxCloneParams{
Model: voicecraftali.ModelMiniMaxSpeech28Turbo,
VoiceID: "myvoice20260824",
AudioURL: "https://example.com/sample.wav",
Text: "今天天气怎么样?",
})
if err != nil {
log.Fatal(err)
}
if result.InputSensitive {
log.Fatalf("输入音频命中风控,类型 %d", result.InputSensitiveType)
}
log.Printf("试听音频:%s", result.DemoAudioURL)
// 复刻出的音色直接用于合成。
audio, err := client.SynthesizeMiniMax(context.Background(), &voicecraftali.MiniMaxParams{
Model: voicecraftali.ModelMiniMaxSpeech28Turbo,
VoiceSetting: voicecraftali.MiniMaxVoiceSetting{VoiceID: result.VoiceID},
}, "这是用复刻音色合成的语音。")
if err != nil {
log.Fatal(err)
}
_ = audio
}
Output:
func (*Client) CreateQwenVoice ¶
func (c *Client) CreateQwenVoice( ctx context.Context, params *CreateQwenVoiceParams, ) (*QwenVoice, error)
CreateQwenVoice 用 Qwen-TTS 复刻一个音色。
样本音频以字节形式传入,本库按协议要求编码成 Data URL。 返回的音色名可直接用作 RealtimeParams.Voice 或 QwenTTSParams.Voice, 但必须搭配 params.TargetModel 指定的那个模型使用。
结果中的 FallbackMode 为 true 时表示复刻是降级完成的,效果可能不理想, 值得在接入流程里显式提示而不是忽略。
voice, err := client.CreateQwenVoice(ctx, &voicecraftali.CreateQwenVoiceParams{
TargetModel: voicecraftali.ModelQwen3TTSVCRealtime,
PreferredName: "myvoice",
Audio: &voicecraftali.QwenVoiceAudio{MIME: "audio/wav", Data: sample},
})
Example ¶
ExampleClient_CreateQwenVoice 展示 Qwen-TTS 的声音复刻。
与 CosyVoice 的复刻是两套接口:样本音频以字节内联提交而不是给 URL。
package main
import (
"context"
"log"
"os"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
sample, err := os.ReadFile("sample.wav")
if err != nil {
log.Fatal(err)
}
voice, err := client.CreateQwenVoice(context.Background(), &voicecraftali.CreateQwenVoiceParams{
TargetModel: voicecraftali.ModelQwen3TTSVCRealtime,
PreferredName: "myvoice",
Audio: &voicecraftali.QwenVoiceAudio{MIME: "audio/wav", Data: sample},
Language: "zh",
})
if err != nil {
log.Fatal(err)
}
// 降级模式意味着样本音频质量不佳,复刻效果可能不理想——值得显式提示。
if voice.FallbackMode {
log.Printf("音色以降级模式创建,原因:%s", voice.FallbackReason)
}
log.Printf("音色 %s,驱动模型 %s", voice.Voice, voice.TargetModel)
}
Output:
func (*Client) CreateVoice ¶
CreateVoice 用 Qwen-Audio-TTS / CosyVoice 复刻一个音色。
返回的音色 ID 可直接用作 SynthesisParams.Voice 或 HTTPSynthesisParams.Voice, 但**必须搭配 params.TargetModel 指定的那个模型使用**,否则合成会失败。
音色创建后要经过审核:刚返回时状态通常是 DEPLOYING,需轮询 GetVoice 直到状态变为 OK 才能用于合成。
voiceID, err := client.CreateVoice(ctx, &voicecraftali.CreateVoiceParams{
TargetModel: voicecraftali.ModelCosyVoiceV3Flash,
Prefix: "myvoice",
URL: "https://example.com/sample.wav",
})
Example ¶
ExampleClient_CreateVoice 展示 Qwen-Audio-TTS / CosyVoice 的声音复刻。
音色创建后要经过审核,状态变为 OK 才能用于合成。
package main
import (
"context"
"log"
"os"
"time"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
// TargetModel 必须与后续合成时使用的模型一致,否则合成会失败。
voiceID, err := client.CreateVoice(ctx, &voicecraftali.CreateVoiceParams{
TargetModel: voicecraftali.ModelCosyVoiceV3Flash,
Prefix: "myvoice",
URL: "https://example.com/sample.wav",
})
if err != nil {
log.Fatal(err)
}
// 轮询到审核通过为止。
for {
detail, err := client.GetVoice(ctx, voiceID)
if err != nil {
log.Fatal(err)
}
if detail.Status.Usable() {
break
}
if detail.Status == voicecraftali.VoiceStatusUndeployed {
log.Fatalf("音色审核未通过:%s", voicecraftali.VoiceStatusDescription(detail.Status))
}
time.Sleep(5 * time.Second)
}
// 复刻音色的用法与系统音色完全一致。
audio, err := client.Synthesize(ctx, &voicecraftali.SynthesisParams{
Model: voicecraftali.ModelCosyVoiceV3Flash, // 与 TargetModel 一致
Voice: voiceID,
}, "这是用复刻音色合成的语音。")
if err != nil {
log.Fatal(err)
}
_ = audio
}
Output:
func (*Client) DeleteDesignedQwenVoice ¶
DeleteDesignedQwenVoice 删除一个 Qwen 设计音色。
func (*Client) DeleteQwenVoice ¶
DeleteQwenVoice 删除一个 Qwen-TTS 复刻音色。
参数是音色名(创建时返回的 Voice),不是 Qwen-Audio-TTS / CosyVoice 那套 voice_id。
func (*Client) DeleteVoice ¶
DeleteVoice 删除一个 Qwen-Audio-TTS / CosyVoice 自定义音色。
声音复刻与声音设计产出的音色都用本方法删除。
func (*Client) DesignQwenVoice ¶
func (c *Client) DesignQwenVoice( ctx context.Context, params *DesignQwenVoiceParams, ) (*DesignedQwenVoice, error)
DesignQwenVoice 用文字描述设计一个 Qwen 音色。
designed, err := client.DesignQwenVoice(ctx, &voicecraftali.DesignQwenVoiceParams{
TargetModel: voicecraftali.ModelQwen3TTSVDRealtime,
VoicePrompt: "沉稳的中年男性,音色低沉浑厚",
PreviewText: "各位听众朋友,大家好",
PreferredName: "announcer",
})
Example ¶
ExampleClient_DesignQwenVoice 展示 Qwen 的声音设计。
Qwen 侧的设计走独立模型,音色列表与详情也是独立的一套。
package main
import (
"context"
"log"
"os"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
designed, err := client.DesignQwenVoice(ctx, &voicecraftali.DesignQwenVoiceParams{
TargetModel: voicecraftali.ModelQwen3TTSVDRealtime,
VoicePrompt: "沉稳的中年男性,音色低沉浑厚",
PreviewText: "各位听众朋友,大家好",
PreferredName: "announcer",
Language: "zh",
})
if err != nil {
log.Fatal(err)
}
// 与声音复刻的音色是两批,用各自的接口管理。
list, err := client.ListDesignedQwenVoices(ctx, &voicecraftali.ListVoicesParams{PageSize: 20})
if err != nil {
log.Fatal(err)
}
log.Printf("共 %d 个设计音色,本次新增 %s", list.TotalCount, designed.Voice)
}
Output:
func (*Client) DesignVoice ¶
func (c *Client) DesignVoice( ctx context.Context, params *DesignVoiceParams, ) (*DesignedVoice, error)
DesignVoice 用文字描述设计一个 Qwen-Audio-TTS / CosyVoice 音色。
与 CreateVoice(声音复刻)的区别是输入:设计给文字描述,复刻给样本音频。 两者产出的音色在服务端是同一批,用 ListVoices、GetVoice、DeleteVoice 统一管理。
返回值带一段试听音频,可用来在落库之前先让人听一耳朵。 音色同样要经过审核,需轮询 GetVoice 直到状态为 OK 才能用于合成。
designed, err := client.DesignVoice(ctx, &voicecraftali.DesignVoiceParams{
TargetModel: voicecraftali.ModelCosyVoiceV35Plus,
VoicePrompt: "沉稳的中年男性,音色低沉浑厚",
PreviewText: "各位听众朋友们大家好,欢迎收听本期节目",
Prefix: "announcer",
})
Example ¶
ExampleClient_DesignVoice 展示用文字描述设计一个音色。
与声音复刻的区别是输入:设计给描述,复刻给样本音频。
package main
import (
"context"
"log"
"os"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
designed, err := client.DesignVoice(ctx, &voicecraftali.DesignVoiceParams{
TargetModel: voicecraftali.ModelCosyVoiceV35Plus,
VoicePrompt: "沉稳的中年男性,音色低沉浑厚",
PreviewText: "各位听众朋友们大家好,欢迎收听本期节目",
Prefix: "announcer", // 设计侧不允许下划线且不超过 10 字符
Preview: &voicecraftali.PreviewOptions{
SampleRate: 24000,
ResponseFormat: voicecraftali.FormatWAV,
},
})
if err != nil {
log.Fatal(err)
}
// 先听一耳朵再决定要不要落库。
if designed.Preview != nil {
if err := os.WriteFile("preview.wav", designed.Preview.Data, 0o600); err != nil {
log.Fatal(err)
}
}
// 设计出的音色与复刻音色共用同一套管理接口。
detail, err := client.GetVoice(ctx, designed.VoiceID)
if err != nil {
log.Fatal(err)
}
log.Printf("音色 %s 设计自「%s」,状态 %s", designed.VoiceID, detail.VoicePrompt, detail.Status)
}
Output:
func (*Client) GetDesignedQwenVoice ¶
func (c *Client) GetDesignedQwenVoice( ctx context.Context, voice string, ) (*DesignedQwenVoiceDetail, error)
GetDesignedQwenVoice 查询 Qwen 设计音色的详情。
声音复刻的 Qwen 侧没有详情接口,声音设计有——这是两者的一处能力差异。
func (*Client) GetVoice ¶
GetVoice 查询 Qwen-Audio-TTS / CosyVoice 自定义音色的详情。
声音复刻与声音设计的音色都用本方法查询,用 VoiceDetail.Designed 区分来源。
常用于轮询审核状态:创建后状态为 DEPLOYING,变为 OK 才能用于合成。
func (*Client) ListDesignedQwenVoices ¶
func (c *Client) ListDesignedQwenVoices( ctx context.Context, params *ListVoicesParams, ) (*DesignedQwenVoiceList, error)
ListDesignedQwenVoices 查询 Qwen 设计音色的列表。
与 ListQwenVoices(声音复刻)是两批不同的音色——它们走的是不同的模型。 params 可为 nil。本接口不支持按前缀筛选。
func (*Client) ListQwenVoices ¶
func (c *Client) ListQwenVoices( ctx context.Context, params *ListVoicesParams, ) (*QwenVoiceList, error)
ListQwenVoices 查询 Qwen-TTS 的复刻音色列表。
与 ListVoices 不同,本接口返回分页信息,且列表项不含审核状态。 params 可为 nil,表示用服务端默认分页。本接口不支持按前缀筛选。
func (*Client) ListVoices ¶
func (c *Client) ListVoices(ctx context.Context, params *ListVoicesParams) ([]VoiceSummary, error)
ListVoices 查询 Qwen-Audio-TTS / CosyVoice 的自定义音色列表。
声音复刻与声音设计产出的音色在服务端是同一批,本方法把它们一并返回; 用 VoiceSummary.Designed 区分来源。
params 可为 nil,表示不筛选、用服务端默认分页。
Example ¶
ExampleClient_ListVoices 展示音色列表与删除。
package main
import (
"context"
"log"
"os"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
voices, err := client.ListVoices(ctx, &voicecraftali.ListVoicesParams{
Prefix: "myvoice",
PageSize: 20,
})
if err != nil {
log.Fatal(err)
}
for _, v := range voices {
log.Printf("%s [%s] %s", v.VoiceID, v.Status, voicecraftali.VoiceStatusDescription(v.Status))
if v.Status == voicecraftali.VoiceStatusUndeployed {
if err := client.DeleteVoice(ctx, v.VoiceID); err != nil {
log.Print(err)
}
}
}
}
Output:
func (*Client) NewRealtimeSession ¶
func (c *Client) NewRealtimeSession( ctx context.Context, params *RealtimeParams, opts ...SessionOption, ) (*Session, error)
NewRealtimeSession 创建一个 Qwen-TTS Realtime 会话(WebSocket 事件流)。
params 会被深拷贝,调用方可以安全地复用同一份参数创建多个会话。
交互模式由 params.Mode 决定:server_commit(默认)由服务端自动判定合成时机, 只需持续 SendText;commit 模式需要调用 Commit 才会触发合成。
session, err := client.NewRealtimeSession(ctx, &voicecraftali.RealtimeParams{
Model: voicecraftali.ModelQwen3TTSFlashRealtime,
Voice: voicecraftali.VoiceCherry,
Mode: voicecraftali.ModeServerCommit,
})
Example ¶
ExampleClient_NewRealtimeSession 展示 Qwen-TTS Realtime 的用法。
package main
import (
"context"
"log"
"os"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
session, err := client.NewRealtimeSession(ctx, &voicecraftali.RealtimeParams{
Model: voicecraftali.ModelQwen3TTSFlashRealtime,
Voice: voicecraftali.VoiceCherry,
Mode: voicecraftali.ModeServerCommit,
LanguageType: voicecraftali.LanguageChinese,
})
if err != nil {
log.Fatal(err)
}
defer session.Close()
go func() {
_ = session.SendText(ctx, "您好,我是千问。")
// server_commit 模式下服务端自动判定合成时机;
// commit 模式则需要显式调用 session.Commit(ctx)。
_ = session.Finish(ctx)
}()
if err := session.Stream(ctx, func(audio []byte) error {
_ = audio
return nil
}); err != nil {
log.Fatal(err)
}
if usage := session.Usage(); usage != nil {
log.Printf("计费字符数 %d,Token 数 %d", usage.Characters, usage.TotalTokens)
}
}
Output:
func (*Client) NewSambertSession ¶
func (c *Client) NewSambertSession( ctx context.Context, params *SambertParams, opts ...SessionOption, ) (*Session, error)
NewSambertSession 创建一个 Sambert 会话(WebSocket 单向流式)。
params 会被深拷贝,调用方可以安全地复用同一份参数创建多个会话。
Sambert 与 CosyVoice / Qwen-Audio-TTS 是两套不同的 API: 待合成的全文必须放在 params.Text 中随建立会话一次性提交, 会话的 SendText 与 Cancel 都返回 ErrUnsupportedOperation, Finish 是空操作(服务端自行结束任务)。读取音频的方式则完全一致。
Sambert 仅在北京地域可用。
session, err := client.NewSambertSession(ctx, &voicecraftali.SambertParams{
Model: voicecraftali.ModelSambertZhichu,
Text: "床前明月光,疑是地上霜。",
})
if err != nil {
return err
}
defer session.Close()
err = session.Stream(ctx, func(audio []byte) error {
_, err := file.Write(audio)
return err
})
Example ¶
ExampleClient_NewSambertSession 展示 Sambert 的用法。
Sambert 是另一套 API:没有 voice 参数(音色由模型名决定), 全文在创建会话时一次性提交,不支持 SendText。
package main
import (
"context"
"errors"
"io"
"log"
"os"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
session, err := client.NewSambertSession(ctx, &voicecraftali.SambertParams{
Model: voicecraftali.ModelSambertZhichu,
Text: "白日依山尽,黄河入海流。",
Format: voicecraftali.FormatWAV,
SampleRate: 16000,
})
if err != nil {
log.Fatal(err)
}
defer session.Close()
// 读取方式与其他 API 完全一致。
for {
audio, err := session.Read()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
log.Fatal(err)
}
_ = audio
}
}
Output:
func (*Client) NewSynthesisSession ¶
func (c *Client) NewSynthesisSession( ctx context.Context, params *SynthesisParams, opts ...SessionOption, ) (*Session, error)
NewSynthesisSession 创建一个 CosyVoice / Qwen-Audio-TTS 会话(WebSocket 双向流式)。
params 会被深拷贝,调用方可以安全地复用同一份参数创建多个会话。
文本通过 Session.SendText 增量送入,送完调用 Session.Finish。 单次 SendText 上限 20000 字符、单个任务累计上限 200000 字符; 两次发送的间隔不应超过 23 秒,否则服务端会断开连接。
Sambert 模型不能用本方法,请改用 NewSambertSession。
session, err := client.NewSynthesisSession(ctx, &voicecraftali.SynthesisParams{
Model: voicecraftali.ModelCosyVoiceV3Flash,
Voice: "longanyang",
Format: voicecraftali.FormatMP3,
})
Example ¶
ExampleClient_NewSynthesisSession 展示 CosyVoice / Qwen-Audio-TTS 的流式合成: 一个 goroutine 送文本,主 goroutine 取音频。
package main
import (
"context"
"log"
"os"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
session, err := client.NewSynthesisSession(ctx, &voicecraftali.SynthesisParams{
Model: voicecraftali.ModelCosyVoiceV3Flash,
Voice: "longanyang",
Format: voicecraftali.FormatMP3,
})
if err != nil {
log.Fatal(err)
}
defer session.Close()
go func() {
// 文本可以分多次送入,边生成边合成。
for _, line := range []string{"床前明月光,疑是地上霜。", "举头望明月,低头思故乡。"} {
if err := session.SendText(ctx, line); err != nil {
log.Print(err)
return
}
}
// 送完必须调用 Finish,否则服务端不会输出剩余音频。
if err := session.Finish(ctx); err != nil {
log.Print(err)
}
}()
file, err := os.Create("output.mp3")
if err != nil {
log.Fatal(err)
}
defer file.Close()
// Stream 会持续把音频交给回调,直到合成结束。
if err := session.Stream(ctx, func(audio []byte) error {
_, err := file.Write(audio)
return err
}); err != nil {
log.Fatal(err)
}
}
Output:
func (*Client) StreamHTTP ¶
func (c *Client) StreamHTTP( ctx context.Context, params *HTTPSynthesisParams, text string, handler func(audio []byte) error, opts ...SessionOption, ) (*SynthesisResult, error)
StreamHTTP 用 Qwen-Audio-TTS / CosyVoice 的非实时 HTTP 接口流式合成。
与 SynthesizeHTTP 的区别:服务端以 SSE 逐段返回 base64 音频分片, handler 每收到一段就被调用一次,适合边合成边写文件或边转发。 handler 返回错误会中止流并把该错误上抛。
开启 params.WordTimestampEnabled 后,进度与字级时间戳通过 opts 中的 WithProgress 回调返回。
返回的 SynthesisResult 来自流的最后一帧,其中 Audio.URL 是完整音频的下载地址, Audio.Data 为空——分片已经通过 handler 交给调用方了。
Example ¶
ExampleClient_StreamHTTP 展示非实时 HTTP 接口的流式模式: 服务端以 SSE 逐段返回音频,边收边写文件。
package main
import (
"context"
"log"
"os"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
file, err := os.Create("output.mp3")
if err != nil {
log.Fatal(err)
}
defer file.Close()
ctx := context.Background()
result, err := client.StreamHTTP(ctx, &voicecraftali.HTTPSynthesisParams{
Model: voicecraftali.ModelCosyVoiceV3Flash,
Voice: "longanyang",
}, "床前明月光,疑是地上霜。", func(audio []byte) error {
_, err := file.Write(audio)
return err
})
if err != nil {
log.Fatal(err)
}
if result.Usage != nil {
log.Printf("计费字符数 %d", result.Usage.Characters)
}
}
Output:
func (*Client) StreamMiniMax ¶
func (c *Client) StreamMiniMax( ctx context.Context, params *MiniMaxParams, text string, handler func(audio []byte) error, ) (*MiniMaxResult, error)
StreamMiniMax 用 MiniMax 的非实时 HTTP 接口流式合成。
handler 每收到一段音频被调用一次(本库已把十六进制解码为字节)。
默认情况下结束帧还会携带一份完整音频,本库**不会**把它再交给 handler, 避免重复;它作为 result.Audio.Data 返回。把 params.ExcludeAggregatedAudio 设为 true 可让服务端不返回这份聚合音频,此时 result.Audio.Data 为空, 完整音频由调用方自行拼接 handler 收到的分片。
func (*Client) StreamQwenTTS ¶
func (c *Client) StreamQwenTTS( ctx context.Context, params *QwenTTSParams, text string, handler func(audio []byte) error, ) (*SynthesisResult, error)
StreamQwenTTS 用 Qwen-TTS 的非实时 HTTP 接口流式合成。
服务端以 SSE 逐段返回 base64 音频分片,handler 每收到一段被调用一次。 返回的 SynthesisResult 来自流的最后一帧。
func (*Client) Synthesize ¶
func (c *Client) Synthesize( ctx context.Context, params *SynthesisParams, text string, ) ([]byte, error)
Synthesize 用 CosyVoice / Qwen-Audio-TTS 把一整段文本合成为完整音频。
内部完成建会话、送文本、结束、读干、关闭的全过程,返回时连接已释放。 需要边合成边消费(低首包延迟)时请改用 NewSynthesisSession。
text 长度上限为 20000 字符(按 rune 计),超限返回 ErrTextTooLong 且不建立连接。
合成过程中出错时返回错误且不返回部分音频:一段残缺的音频比没有音频更难排查, 也容易被误当成完整结果写进文件。
audio, err := client.Synthesize(ctx, &voicecraftali.SynthesisParams{
Model: voicecraftali.ModelCosyVoiceV3Flash,
Voice: "longanyang",
Format: voicecraftali.FormatMP3,
}, "床前明月光,疑是地上霜。")
Example ¶
ExampleClient_Synthesize 展示短文本一次性合成。
package main
import (
"context"
"log"
"os"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
audio, err := client.Synthesize(context.Background(), &voicecraftali.SynthesisParams{
Model: voicecraftali.ModelCosyVoiceV3Flash,
Voice: "longanyang",
Format: voicecraftali.FormatMP3,
}, "床前明月光,疑是地上霜。")
if err != nil {
log.Fatal(err)
}
if err := os.WriteFile("output.mp3", audio, 0o600); err != nil {
log.Fatal(err)
}
}
Output:
Example (LaTeX) ¶
ExampleClient_Synthesize_laTeX 展示 LaTeX 公式朗读。
LaTeX 不需要任何参数开关——把公式用 $...$ 包起来写进文本即可。
package main
import (
"context"
"log"
"os"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
// 用反引号原样字符串,避免为 LaTeX 的反斜杠再转义一层。
text := `这是一元二次方程的求根公式:$x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}$,请仔细计算。`
audio, err := client.Synthesize(context.Background(), &voicecraftali.SynthesisParams{
Model: voicecraftali.ModelCosyVoiceV3Flash, // LaTeX 与 SSML 是同一批模型
Voice: "longanyang",
}, text)
if err != nil {
log.Fatal(err)
}
_ = audio
}
Output:
func (*Client) SynthesizeHTTP ¶
func (c *Client) SynthesizeHTTP( ctx context.Context, params *HTTPSynthesisParams, text string, ) (*SynthesisResult, error)
SynthesizeHTTP 用 Qwen-Audio-TTS / CosyVoice 的非实时 HTTP 接口合成整段文本。
**返回的是音频下载地址而不是音频字节**:该接口的非流式模式把音频存到 OSS 并返回一个有效期 24 小时的 URL。需要字节时调用 result.Audio.Download:
result, err := client.SynthesizeHTTP(ctx, params, "床前明月光")
if err != nil {
return err
}
audio, err := result.Audio.Download(ctx, client)
需要边合成边拿到音频字节时用 StreamHTTP。
该接口仅在北京地域可用。
Example ¶
ExampleClient_SynthesizeHTTP 展示 Qwen-Audio-TTS / CosyVoice 的非实时 HTTP 合成。
注意该接口返回的是音频下载地址而不是字节。
package main
import (
"context"
"log"
"os"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
result, err := client.SynthesizeHTTP(ctx, &voicecraftali.HTTPSynthesisParams{
Model: voicecraftali.ModelQwenAudioTTSFlash,
Voice: voicecraftali.VoiceLongAnHuan,
Format: voicecraftali.FormatWAV,
SampleRate: 24000,
}, "我家的后面有一个很大的花园。")
if err != nil {
log.Fatal(err)
}
// 非流式模式返回的是有效期 24 小时的下载地址;需要字节时再取一次。
log.Printf("音频地址 %s(%s 过期)", result.Audio.URL, result.Audio.ExpiresAt)
audio, err := result.Audio.Download(ctx, client)
if err != nil {
log.Fatal(err)
}
if err := os.WriteFile("output.wav", audio, 0o600); err != nil {
log.Fatal(err)
}
}
Output:
func (*Client) SynthesizeMiniMax ¶
func (c *Client) SynthesizeMiniMax( ctx context.Context, params *MiniMaxParams, text string, ) (*MiniMaxResult, error)
SynthesizeMiniMax 用 MiniMax 的非实时 HTTP 接口合成整段文本。
与另两套 HTTP 接口不同,**MiniMax 直接返回音频字节**(线上是十六进制编码, 本库已解码),不返回下载地址,因此 result.Audio.Data 可直接使用。
text 长度上限 10000 字符;超过 3000 字符时官方推荐改用 StreamMiniMax。
Example ¶
ExampleClient_SynthesizeMiniMax 展示 MiniMax 的非实时 HTTP 合成。
与另两套 HTTP 接口不同,MiniMax 直接返回音频字节(线上是十六进制编码, 本库已解码),不需要再下载一次。
package main
import (
"context"
"log"
"os"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
result, err := client.SynthesizeMiniMax(context.Background(), &voicecraftali.MiniMaxParams{
Model: voicecraftali.ModelMiniMaxSpeech28HD,
VoiceSetting: voicecraftali.MiniMaxVoiceSetting{
VoiceID: "male-qn-qingse",
Speed: 1.0,
Emotion: voicecraftali.EmotionHappy,
},
AudioSetting: voicecraftali.MiniMaxAudioSetting{
Format: voicecraftali.FormatMP3,
SampleRate: 32000,
Bitrate: 128000,
},
}, "今天是不是很开心呀,当然了!")
if err != nil {
log.Fatal(err)
}
// MiniMax 直接给字节,Audio.Data 可直接使用。
if err := os.WriteFile("output.mp3", result.Audio.Data, 0o600); err != nil {
log.Fatal(err)
}
log.Printf("时长 %d ms,链路 %s", result.ExtraInfo.AudioLength, result.TraceID)
}
Output:
func (*Client) SynthesizeQwenTTS ¶
func (c *Client) SynthesizeQwenTTS( ctx context.Context, params *QwenTTSParams, text string, ) (*SynthesisResult, error)
SynthesizeQwenTTS 用 Qwen-TTS 的非实时 HTTP 接口合成整段文本。
**返回的是音频下载地址而不是音频字节**,需要字节时调用 result.Audio.Download,理由同 SynthesizeHTTP。
文本长度上限:qwen-tts 系列为 512 Token,其余模型为 600 字符。 该上限由服务端判定——Token 数无法在客户端准确计算,因此本库不做本地拦截, 超限会返回服务端的 InvalidInputLength 错误。
Example ¶
ExampleClient_SynthesizeQwenTTS 展示 Qwen-TTS 的非实时 HTTP 合成。
该接口的参数集很小:只有文本、音色、语种与指令。
package main
import (
"context"
"log"
"os"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
result, err := client.SynthesizeQwenTTS(ctx, &voicecraftali.QwenTTSParams{
Model: voicecraftali.ModelQwen3TTSFlash,
Voice: voicecraftali.VoiceCherry,
LanguageType: voicecraftali.LanguageChinese,
}, "那我来给大家推荐一款T恤。")
if err != nil {
log.Fatal(err)
}
audio, err := result.Audio.Download(ctx, client)
if err != nil {
log.Fatal(err)
}
_ = audio
}
Output:
func (*Client) SynthesizeRealtime ¶
func (c *Client) SynthesizeRealtime( ctx context.Context, params *RealtimeParams, text string, ) ([]byte, error)
SynthesizeRealtime 用 Qwen-TTS Realtime 把一整段文本合成为完整音频。
内部会在结束前显式提交一次文本缓冲区,因此 commit 与 server_commit 两种模式下行为一致。
audio, err := client.SynthesizeRealtime(ctx, &voicecraftali.RealtimeParams{
Model: voicecraftali.ModelQwen3TTSFlashRealtime,
Voice: voicecraftali.VoiceCherry,
}, "床前明月光,疑是地上霜。")
func (*Client) SynthesizeSambert ¶
func (c *Client) SynthesizeSambert( ctx context.Context, params *SambertParams, text string, ) ([]byte, error)
SynthesizeSambert 用 Sambert 把一整段文本合成为完整音频。
与 Synthesize 的区别只在参数类型:Sambert 没有 voice 参数(音色由模型名决定), 且全文随建立会话一次性提交。params.Text 必须为空——待合成文本由 text 参数提供。
audio, err := client.SynthesizeSambert(ctx, &voicecraftali.SambertParams{
Model: voicecraftali.ModelSambertZhichu,
}, "床前明月光,疑是地上霜。")
type Code ¶
type Code string
Code 是阿里云百炼返回的错误码。
同一类问题在不同 API 下的码名风格不同:两套 WebSocket 合成 API 用 驼峰或点分驼峰(如 InvalidApiKey、Throttling.RateQuota),Qwen-TTS Realtime (Qwen-TTS Realtime)用蛇形(如 invalid_api_key、limit_requests)。 本包把两种风格都收录为常量,调用方无需自行做风格归一。
const ( // CodeInvalidParameter 请求参数不合法。 CodeInvalidParameter Code = "InvalidParameter" // CodeInvalidParameterDataInspection 内容审核过程中下载媒体资源超时。 CodeInvalidParameterDataInspection Code = "InvalidParameter.DataInspection" // CodeInvalidInputLength 输入长度超出限制。 CodeInvalidInputLength Code = "InvalidInputLength" // CodeBadRequestEmptyInput 请求缺少 input 参数。 CodeBadRequestEmptyInput Code = "BadRequest.EmptyInput" // CodeBadRequestEmptyModel 请求缺少 model 参数。 CodeBadRequestEmptyModel Code = "BadRequest.EmptyModel" // CodeBadRequestEmptyParameters 请求缺少 parameters 参数。 CodeBadRequestEmptyParameters Code = "BadRequest.EmptyParameters" // CodeBadRequestIllegalInput 入参格式不符合 JSON 要求。 CodeBadRequestIllegalInput Code = "BadRequest.IllegalInput" // CodeBadRequestTooLarge 请求体超出大小限制。 CodeBadRequestTooLarge Code = "BadRequest.TooLarge" // CodeBadRequestVoiceNotFound 指定的音色不存在。 CodeBadRequestVoiceNotFound Code = "BadRequest.VoiceNotFound" // CodeBadRequestResourceNotExist 引用的资源不存在。 CodeBadRequestResourceNotExist Code = "BadRequest.ResourceNotExist" // CodeDataInspectionFailed 输入或输出疑似包含敏感内容被拦截。 CodeDataInspectionFailed Code = "DataInspectionFailed" // CodeArrearage 账号欠费导致访问被拒绝。 CodeArrearage Code = "Arrearage" // CodeClientDisconnect 任务结束前客户端主动断开了连接。 CodeClientDisconnect Code = "ClientDisconnect" // CodeUnsupportedOperation 不支持的操作。 CodeUnsupportedOperation Code = "UnsupportedOperation" // CodeInvalidAPIKey API Key 填写错误。 CodeInvalidAPIKey Code = "InvalidApiKey" // CodeNotAuthorized 未授权。 CodeNotAuthorized Code = "NOT AUTHORIZED" // CodeAccessDenied 无权限访问。 CodeAccessDenied Code = "AccessDenied" // CodeAccessDeniedUnpurchased 未开通阿里云百炼服务。 CodeAccessDeniedUnpurchased Code = "AccessDenied.Unpurchased" // CodeModelAccessDenied 无权限调用该模型。 CodeModelAccessDenied Code = "Model.AccessDenied" // CodeWorkspaceAccessDenied 无权限访问该业务空间的模型或应用。 CodeWorkspaceAccessDenied Code = "Workspace.AccessDenied" // CodeEndpointAccessDenied 调用端点不可用,通常因模型已下线。 CodeEndpointAccessDenied Code = "Endpoint.AccessDenied" // CodeAllocationQuotaFreeTierOnly 免费额度已用尽。 CodeAllocationQuotaFreeTierOnly Code = "AllocationQuota.FreeTierOnly" // CodeModelNotFound 模型不存在或无权访问。 CodeModelNotFound Code = "ModelNotFound" // CodeWorkspaceNotFound 业务空间不存在。 CodeWorkspaceNotFound Code = "WorkSpaceNotFound" // CodeNotFound 要查询或操作的资源不存在。 CodeNotFound Code = "NotFound" // CodeConflict 资源冲突,通常是重名。 CodeConflict Code = "Conflict" // CodeThrottling 接口调用触发限流。 CodeThrottling Code = "Throttling" // CodeThrottlingRateQuota 调用频率(RPS/RPM)触发限流。 CodeThrottlingRateQuota Code = "Throttling.RateQuota" // CodeThrottlingBurstRate 调用频率骤增触发系统稳定性保护。 CodeThrottlingBurstRate Code = "Throttling.BurstRate" // CodeThrottlingAllocationQuota Token 消耗(TPS/TPM)触发限流,或资源数量达上限。 CodeThrottlingAllocationQuota Code = "Throttling.AllocationQuota" // CodeThrottlingConcurrency 并发请求数超出平台上限。 CodeThrottlingConcurrency Code = "Throttling.Concurrency" // CodeResourceExhausted 资源耗尽。 CodeResourceExhausted Code = "ResourceExhausted" // CodeLimitRequests 请求数超限。 CodeLimitRequests Code = "LimitRequests" // CodeCommodityNotPurchased 未购买对应商品。 CodeCommodityNotPurchased Code = "CommodityNotPurchased" // CodePrepaidBillOverdue 预付费账单逾期。 CodePrepaidBillOverdue Code = "PrepaidBillOverdue" // CodePostpaidBillOverdue 后付费账单逾期。 CodePostpaidBillOverdue Code = "PostpaidBillOverdue" // CodeInternalError 服务端内部错误。 CodeInternalError Code = "InternalError" // CodeInternalErrorAlgo 推理服务异常。 CodeInternalErrorAlgo Code = "InternalError.Algo" // CodeInternalErrorTimeout 服务端内部超时。 CodeInternalErrorTimeout Code = "InternalError.Timeout" // CodeSystemError 系统错误。 CodeSystemError Code = "SystemError" // CodeModelServiceFailed 模型服务调用失败。 CodeModelServiceFailed Code = "ModelServiceFailed" // CodeRequestTimeOut 请求超时。 CodeRequestTimeOut Code = "RequestTimeOut" // CodeResponseTimeout 响应流超时。 CodeResponseTimeout Code = "ResponseTimeout" CodeServiceUnavailableError Code = "ServiceUnavailableError" CodeModelUnavailable Code = "ModelUnavailable" )
WebSocket 合成 API 风格返回码(CosyVoice / Qwen-Audio-TTS / Sambert,驼峰命名)。
const ( // CodeInvalidValue 参数取值不合法,例如所选音色与模型不匹配。 CodeInvalidValue Code = "invalid_value" // CodeInvalidRequestError 请求不合法。 CodeInvalidRequestError Code = "invalid_request_error" // CodeDataInspectionFailedSnake 输入或输出疑似包含敏感内容被拦截。 CodeDataInspectionFailedSnake Code = "data_inspection_failed" // CodeInvalidAPIKeySnake API Key 填写错误。 CodeInvalidAPIKeySnake Code = "invalid_api_key" // CodeAccessDeniedSnake 无权限访问。 CodeAccessDeniedSnake Code = "access_denied" // CodeModelNotFoundSnake 模型不存在或无权访问。 CodeModelNotFoundSnake Code = "model_not_found" // CodeModelNotSupported 模型不支持该调用方式。 CodeModelNotSupported Code = "model_not_supported" // CodeLimitRequestsSnake 调用频率触发限流。 CodeLimitRequestsSnake Code = "limit_requests" // CodeLimitBurstRate 调用频率骤增触发系统稳定性保护。 CodeLimitBurstRate Code = "limit_burst_rate" // CodeInsufficientQuota 配额不足。 CodeInsufficientQuota Code = "insufficient_quota" // CodeInternalErrorSnake 服务端内部错误。 CodeInternalErrorSnake Code = "internal_error" )
Qwen-TTS Realtime 风格返回码(蛇形命名)。
type CreateQwenVoiceParams ¶
type CreateQwenVoiceParams struct {
// TargetModel 是驱动该音色的语音合成模型。必填。
// 必须与后续合成时使用的模型一致。
//
// 本接口只接受 Qwen-TTS 声音复刻的三个模型:ModelQwen3TTSVC、
// ModelQwen3TTSVCRealtime、ModelQwen3TTSVCRealtimePrev——都是 qwen3-tts-vc-*
// 前缀。声音设计的 qwen3-tts-vd-* 在这里用不了。
// 全部可用取值见 VoiceCloneTargetModels。
TargetModel string
// PreferredName 是音色名称前缀,仅允许数字、英文字母与下划线,不超过 16 个字符。必填。
PreferredName string
// Audio 是样本音频。必填。
Audio *QwenVoiceAudio
// Language 是样本音频的语种,零值表示不设置(服务端默认 zh)。
Language string
}
CreateQwenVoiceParams 是 Qwen-TTS 声音复刻的创建参数。
与 Qwen-Audio-TTS / CosyVoice 的复刻是两套不同的接口: 样本音频以 Data URL 内联提交而不是给 URL,音色名字段叫 preferred_name 而非 prefix, 语种字段是单值 language 而非数组 language_hints。
type CreateVoiceParams ¶
type CreateVoiceParams struct {
// TargetModel 是驱动该音色的语音合成模型。必填。
//
// 它**必须与后续合成时使用的模型一致**,否则合成会失败——
// 这是复刻音色最常见的踩坑点。
//
// 取值参见 VoiceCloneTargetModels:声音复刻与声音设计能用的模型不是同一批,
// 例如 cosyvoice-v2 与 cosyvoice-v1 只能复刻、不能设计。本字段只校验非空,
// 要在发请求前拦下错配,调用方自己用 SupportsVoiceClone 判断。
TargetModel string
// Prefix 是音色名称前缀,仅允许数字、英文字母与下划线,不超过 10 个字符。必填。
// 服务端会在其基础上拼出完整的 voice_id(形如 <target_model>-<prefix>-<uuid>)。
//
// 上限 10 与 Qwen-TTS 侧 CreateQwenVoiceParams.PreferredName 的 16 不同,
// 是两套服务端规则,不是笔误。
Prefix string
// URL 是用于复刻的音频文件地址,要求公网可访问。必填。
URL string
// LanguageHints 辅助模型识别样本音频的语种以更准确地提取音色特征。
// 服务端只处理第一个元素,默认 ["zh"]。
// 设置的语种与实际音频不符时服务端会忽略该设置并自动检测。
LanguageHints []string
// MaxPromptAudioLength 是音频预处理后用于复刻的参考音频最大时长(秒),
// 取值范围 [3.0, 30.0],零值表示不设置(服务端默认 10.0)。
MaxPromptAudioLength float64
// EnablePreprocess 开启音频预处理(降噪、音频增强、音量规整)。
// 有背景噪音时建议开启;安静环境建议关闭以最大程度还原音色。
EnablePreprocess bool
// EnableVolumeNormalization 对样本音频做音量归一化。
//
// 用指针而非 bool:该字段在协议上是**字符串** "true"/"false",
// 且开启与否会改变合成音量,因此需要区分「显式关闭」与「不设置」。
EnableVolumeNormalization *bool
}
CreateVoiceParams 是 Qwen-Audio-TTS / CosyVoice 声音复刻的创建参数。
type Credential ¶
type Credential struct {
// Value 是 API Key 本身,会以 "bearer <Value>" 的形式写入 Authorization 头。
Value string
// ExpiresAt 是该凭据的绝对过期时间。
// 零值表示永不过期(适合本身就是长期密钥、但调用方希望从外部注入的场景)。
ExpiresAt time.Time
}
Credential 是一份用于握手鉴权的凭据。
type CredentialProvider ¶
type CredentialProvider func(ctx context.Context) (Credential, error)
CredentialProvider 由调用方实现,用于提供动态凭据。
适用于短期密钥、STS 临时凭证、密钥托管系统轮转等场景。本库负责缓存与 并发去重:在凭据过期前不会重复调用 provider,且并发请求只会触发一次调用。
provider 应当尊重传入的 ctx;返回空 Value 会被判为 ErrInvalidCredential 且不入缓存。
type DesignQwenVoiceParams ¶
type DesignQwenVoiceParams struct {
// TargetModel 是驱动该音色的语音合成模型,如 ModelQwen3TTSVDRealtime。必填。
//
// 本接口只接受 Qwen-TTS 声音设计的三个模型:ModelQwen3TTSVD、
// ModelQwen3TTSVDRealtime、ModelQwen3TTSVDRealtimePrev——都是 qwen3-tts-vd-*
// 前缀。声音复刻的 qwen3-tts-vc-* 在这里用不了,反之亦然。
// 全部可用取值见 VoiceDesignTargetModels。
TargetModel string
// VoicePrompt 是声音描述文本。必填。不超过 2048 字符。
VoicePrompt string
// PreviewText 是预览音频对应的文本。必填。不超过 1024 字符。
PreviewText string
// PreferredName 是音色名称前缀。必填。
// 仅允许数字、英文字母与下划线,不超过 16 个字符。
PreferredName string
// Language 指定生成音色的语言倾向,零值表示不设置(服务端默认 zh)。
// 该语种需与 PreviewText 的语种一致。
Language string
// Preview 配置预览音频的采样率与格式。
Preview *PreviewOptions
}
DesignQwenVoiceParams 是 Qwen 声音设计的参数。
与 Qwen 声音复刻走的是**不同的模型**(qwen-voice-design 对 qwen-voice-enrollment),因此音色列表、详情、删除也都是独立的一套。
type DesignVoiceParams ¶
type DesignVoiceParams struct {
// TargetModel 是驱动该音色的语音合成模型。必填。
// 必须与后续合成时使用的模型一致,否则合成会失败。
//
// 取值参见 VoiceDesignTargetModels,它比声音复刻的清单短:
// cosyvoice-v2、cosyvoice-v1 与 Qwen-Audio-Realtime 只能复刻、不能设计。
// 本字段只校验非空,要在发请求前拦下错配,调用方自己用
// SupportsVoiceDesign 判断。
TargetModel string
// VoicePrompt 是声音描述文本,如"沉稳的中年男性,音色低沉浑厚"。必填。
// 仅支持中文与英文,不超过 500 字符。
VoicePrompt string
// PreviewText 是预览音频对应的文本。必填。
// 长度需在 [15, 200] 字符之间,支持中文与英文。
PreviewText string
// Prefix 是音色名称前缀。必填。
//
// 约束比声音复刻更严:**仅允许数字与英文字母(不含下划线)**,
// 且不超过 10 个字符。生成的音色名形如 {target_model}-vd-{prefix}-{唯一标识}。
Prefix string
// LanguageHints 指定生成音色的语言倾向,仅支持 zh 与 en,默认 ["zh"]。
// 服务端只处理第一个元素,且该语种需与 PreviewText 的语种一致。
LanguageHints []string
// Preview 配置预览音频的采样率与格式。
Preview *PreviewOptions
}
DesignVoiceParams 是 Qwen-Audio-TTS / CosyVoice 声音设计的参数。
与声音复刻(CreateVoiceParams)走的是同一个接口,靠 input 字段区分: 设计给的是文字描述而不是样本音频。若干约束比复刻更严,见各字段说明。
type DesignedQwenVoice ¶
type DesignedQwenVoice struct {
// Voice 是设计出的音色名,可直接用作 RealtimeParams.Voice 或 QwenTTSParams.Voice。
Voice string
// TargetModel 是驱动该音色的语音合成模型。
TargetModel string
// Preview 是试听音频,服务端未返回时为 nil。
Preview *PreviewAudio
}
DesignedQwenVoice 是一次 Qwen 声音设计的结果。
type DesignedQwenVoiceDetail ¶
type DesignedQwenVoiceDetail struct {
// Voice 是音色名。
Voice string
// TargetModel 是驱动该音色的语音合成模型。
TargetModel string
// Language 是生成音色时的语言倾向。
Language string
// GmtCreate 是创建时间。
GmtCreate string
// GmtModified 是修改时间。
GmtModified string
}
DesignedQwenVoiceDetail 是 Qwen 设计音色的详情。
type DesignedQwenVoiceList ¶
type DesignedQwenVoiceList struct {
// Voices 是本页音色。
Voices []DesignedQwenVoiceSummary
// PageIndex 是当前页码索引。
PageIndex int
// PageSize 是每页数据条数。
PageSize int
// TotalCount 是音色总数。
TotalCount int
}
DesignedQwenVoiceList 是 Qwen 设计音色列表的分页结果。
type DesignedQwenVoiceSummary ¶
type DesignedQwenVoiceSummary struct {
// Voice 是音色名。
Voice string `json:"voice"`
// GmtCreate 是创建时间。
GmtCreate string `json:"gmt_create"`
// GmtModified 是修改时间。
GmtModified string `json:"gmt_modified"`
// Language 是生成音色时的语言倾向。
Language string `json:"language"`
// TargetModel 是驱动该音色的语音合成模型。
TargetModel string `json:"target_model"`
// VoicePrompt 是声音描述文本。
VoicePrompt string `json:"voice_prompt"`
// PreviewText 是预览文本。
PreviewText string `json:"preview_text"`
}
DesignedQwenVoiceSummary 是 Qwen 设计音色列表中的一项。
type DesignedVoice ¶
type DesignedVoice struct {
// VoiceID 是设计出的音色 ID,可直接用作合成参数中的 Voice。
VoiceID string
// TargetModel 是驱动该音色的语音合成模型。
TargetModel string
// Preview 是试听音频,服务端未返回时为 nil。
Preview *PreviewAudio
}
DesignedVoice 是一次 Qwen-Audio-TTS / CosyVoice 声音设计的结果。
type EventError ¶
type EventError struct {
// EventID 是服务端事件 ID。
EventID string
// Code 取服务端 error.code。可传给 CodeDescription 获取中文说明。
Code Code
// Message 取服务端 error.message,是服务端原始文案。
Message string
}
EventError 表示 Qwen-TTS Realtime 的服务端返回了 error 事件。
func (*EventError) Error ¶
func (e *EventError) Error() string
type HTTPError ¶
type HTTPError struct {
// StatusCode 是 HTTP 响应状态码。
StatusCode int
// Code 取响应体的 code 字段。可传给 CodeDescription 获取中文说明。
Code Code
// Message 取响应体的 message 字段,是服务端原始文案。
Message string
// RequestID 是本次调用的唯一标识符,提交工单排查时需要提供。
RequestID string
// RetryAfter 是服务端 Retry-After 响应头给出的建议等待时长,
// 服务端未给该头、或其取值无法解析时为 0。
//
// 本库不依据该字段自行重试——重试与退避的策略由调用方决定,
// 这里只是把服务端已经给出的依据交出来,省得调用方拍一个数字:
//
// if voicecraftali.Retryable(err) {
// wait := time.Second
// if httpErr, ok := errors.AsType[*voicecraftali.HTTPError](err); ok && httpErr.RetryAfter > 0 {
// wait = httpErr.RetryAfter
// }
// time.Sleep(wait)
// }
RetryAfter time.Duration
}
HTTPError 表示非实时 HTTP 合成接口返回了失败响应。
阿里云在 HTTP 侧用响应体里的 code / message 表达业务错误, 与 WebSocket 侧的 task-failed / error 事件是不同的载体,因此单列一个类型。
type HTTPSynthesisParams ¶
type HTTPSynthesisParams struct {
// Model 是模型名。必填。取值见 httpSynthesisModels,不含 cosyvoice-v1。
Model string
// Voice 是音色。必填。
Voice string
// Format 是输出音频格式,零值表示不设置。
//
// 服务端在不设置时给什么格式,本路径**未经实测**;WebSocket 合成路径
// (SynthesisParams.Format)实测返回的是裸 PCM 而非 mp3。要拿到确定的格式,
// 显式指定即可。
Format AudioFormat
// SampleRate 是输出采样率(Hz),取值 8000、16000、22050、24000、44100 或 48000,
// 零值表示不设置(服务端默认 22050)。
SampleRate int
// Volume 是音量,取值范围 [0, 100],服务端默认 50。
// 用指针的原因同 SynthesisParams.Volume。
Volume *int
// Rate 是语速,取值范围 [0.5, 2.0],零值表示不设置。
Rate float64
// Pitch 是语调,取值范围 [0.5, 2.0],零值表示不设置。
Pitch float64
// BitRate 是码率(kbps),取值范围 [6, 510],**仅 opus 格式有效**,
// 设了别的格式(含 Format 留零值)会被拒绝。
// 零值表示不设置(服务端默认 32)。
BitRate int
// Seed 是随机数种子,取值范围 [0, 65535]。
Seed int
// EnableSSML 启用 SSML 解析,此时 text 需为 SSML 标记文本。
//
// 仅 CosyVoice 系列的部分模型支持(见 SupportsSSML)。
// 拼接 SSML 时外部文本需先过 EscapeSSMLText 转义。
EnableSSML bool
// WordTimestampEnabled 启用字级别时间戳,仅流式模式下有效,
// 时间戳随流式回调的 Progress 返回。
WordTimestampEnabled bool
// EnableMarkdownFilter 在合成前过滤文本中的 Markdown 标记符号。
EnableMarkdownFilter bool
// LanguageHints 指定目标语言以提升合成效果,服务端只处理第一个元素。
LanguageHints []string
// Instruction 是自然语言指令,用于控制方言、情感或角色等合成效果。
Instruction string
// EnableAIGCTag 在生成的音频中嵌入 AIGC 隐性标识。
EnableAIGCTag bool
// AIGCPropagator 设置 AIGC 隐性标识的 ContentPropagator 字段。
AIGCPropagator string
// AIGCPropagateID 设置 AIGC 隐性标识的 PropagateID 字段。
AIGCPropagateID string
// HotFix 是文本热修复配置。cosyvoice-v2 不支持。
HotFix *HotFix
}
HTTPSynthesisParams 是 Qwen-Audio-TTS / CosyVoice 非实时 HTTP 合成的参数。
与 WebSocket 侧的 SynthesisParams 单列成两个类型,是因为线格式不同: 本类型的所有字段都在 input 内,而不是独立的 parameters 对象。
该接口仅在北京地域可用。
type HandshakeError ¶
type HandshakeError struct {
// StatusCode 是握手响应的 HTTP 状态码;握手未走到响应阶段时为 0。
StatusCode int
// URL 是本次握手的目标地址,不含任何凭据。
URL string
// contains filtered or unexported fields
}
HandshakeError 表示 WebSocket 握手失败。
鉴权在握手阶段完成,因此 API Key 无效或缺失表现为 StatusCode 401 或 403。
func (*HandshakeError) Error ¶
func (e *HandshakeError) Error() string
func (*HandshakeError) Unwrap ¶
func (e *HandshakeError) Unwrap() error
Unwrap 返回底层 dial 错误,使 errors.Is / errors.As 能穿透到根因。
type HotFix ¶
type HotFix struct {
// Pronunciation 是自定义发音,每个元素形如 {"天气": "tian1 qi4"}。
Pronunciation []map[string]string `json:"pronunciation,omitempty"`
// Replace 是文本替换,每个元素形如 {"今天": "金天"},替换后的文本作为实际合成内容。
Replace []map[string]string `json:"replace,omitempty"`
}
HotFix 是文本热修复配置,用于纠正指定词语的发音或在合成前替换文本。
cosyvoice-v2 与 cosyvoice-v1 不支持该功能,Sambert 没有该参数。
type ListVoicesParams ¶
type ListVoicesParams struct {
// Prefix 按前缀筛选音色,留空表示不筛选。
Prefix string
// PageIndex 是页码索引,从 0 开始。
PageIndex int
// PageSize 是每页数据条数,零值表示用服务端默认。
PageSize int
}
ListVoicesParams 是查询音色列表的参数。
type MiniMaxAudioSetting ¶
type MiniMaxAudioSetting struct {
// SampleRate 是采样率(Hz),零值表示不设置(服务端默认 32000)。
SampleRate int
// Bitrate 是码率(bps,注意单位不是 kbps),零值表示不设置。
Bitrate int
// Format 是音频格式,取值 mp3、pcm、flac 或 wav,零值表示不设置。
Format AudioFormat
// Channel 是声道数,取值 1 或 2,零值表示不设置(服务端默认 1)。
Channel int
// ForceCBR 以恒定码率编码。仅流式输出且格式为 mp3 时生效。
ForceCBR bool
}
MiniMaxAudioSetting 是 MiniMax 的音频编码设置。
type MiniMaxCloneParams ¶
type MiniMaxCloneParams struct {
// Model 是合成试听音频所用的模型,见 ModelMiniMaxSpeech28HD 等常量或
// MiniMaxModels。必填。
//
// 注意这里叫 Model 而不是 TargetModel:MiniMax 的复刻走独立接口,
// 不使用 target_model,因此 MiniMax 模型名**不出现在**
// VoiceCloneTargetModels 里,SupportsVoiceClone 对它们也返回 false。
// 与另外两套复刻不同,本字段做白名单校验,取值必须是 MiniMaxModels 之一。
Model string
// VoiceID 是自定义的音色 ID。必填,且**全局唯一**。
//
// 约束:长度 [8, 256],首字符为英文字母,允许数字、字母、连字符与下划线,
// 末位不可为连字符或下划线。与已有 ID 重复会被服务端拒绝,
// 建议带上时间戳等个性化信息。
VoiceID string
// AudioURL 是待复刻的音频地址。必填。
// 要求 mp3/m4a/wav、时长 10 秒到 5 分钟、不超过 20 MB。
AudioURL string
// Text 是试听内容,不超过 1000 字符。必填。
//
// 试听音频按所选模型的合成单价**额外计费**。
Text string
// ClonePrompt 是可选的示例音频,用于提升相似度与稳定性。
ClonePrompt *MiniMaxClonePrompt
// LanguageBoost 增强对指定小语种或方言的识别能力,可设为 "auto"。
LanguageBoost string
// NeedNoiseReduction 开启降噪。
NeedNoiseReduction bool
// NeedVolumeNormalization 开启音量归一化。
NeedVolumeNormalization bool
// AIGCWatermark 在试听音频末尾添加音频节奏标识。
AIGCWatermark bool
}
MiniMaxCloneParams 是 MiniMax 声音复刻的参数。
与阿里云自研的两套复刻接口差异很大:voice_id 由调用方自定义且全局唯一, 必须提供一段试听文本(会按合成价另行计费),且不提供列表、详情、更新操作。
type MiniMaxClonePrompt ¶
type MiniMaxClonePrompt struct {
// PromptAudio 是示例音频地址。要求 mp3/m4a/wav、时长小于 8 秒、不超过 20 MB。
PromptAudio string `json:"prompt_audio,omitempty"`
// PromptText 是示例音频对应的文本,需与音频内容一致且句末带标点。
PromptText string `json:"prompt_text,omitempty"`
}
MiniMaxClonePrompt 是 MiniMax 声音复刻的示例音频, 提供它有助于提升音色相似度与稳定性。
type MiniMaxCloneResult ¶
type MiniMaxCloneResult struct {
// RequestID 是本次调用的唯一标识符。
RequestID string
// VoiceID 是复刻出的音色 ID,即请求中自定义的那个值,
// 可直接用作 MiniMaxVoiceSetting.VoiceID。
VoiceID string
// DemoAudioURL 是试听音频的地址。
DemoAudioURL string
// InputSensitive 为 true 表示输入音频命中了风控。
InputSensitive bool
// InputSensitiveType 是命中风控的类型,见 SensitiveNone 等常量。
InputSensitiveType int
// Usage 是本次调用的计费信息——试听音频按合成价另行计费。
Usage *Usage
}
MiniMaxCloneResult 是一次 MiniMax 声音复刻的结果。
type MiniMaxExtraInfo ¶
type MiniMaxExtraInfo struct {
AudioChannel int `json:"audio_channel"`
AudioFormat string `json:"audio_format"`
AudioLength int `json:"audio_length"`
AudioSampleRate int `json:"audio_sample_rate"`
AudioSize int `json:"audio_size"`
Bitrate int `json:"bitrate"`
UsageCharacters int `json:"usage_characters"`
WordCount int `json:"word_count"`
}
MiniMaxExtraInfo 是 MiniMax 返回的音频元信息。
type MiniMaxParams ¶
type MiniMaxParams struct {
// Model 是模型名,见 ModelMiniMaxSpeech28HD 等常量或 MiniMaxModels。必填。
// 本字段做白名单校验,取值必须是 MiniMaxModels 之一。
Model string
// VoiceSetting 是音色与韵律设置。必填。
VoiceSetting MiniMaxVoiceSetting
// AudioSetting 是音频编码设置,各字段留零值即用服务端默认。
AudioSetting MiniMaxAudioSetting
// TimbreWeights 是混合音色配置,最多 4 种。
// 使用混合音色时 VoiceSetting.VoiceID 需留空。
TimbreWeights []MiniMaxTimbreWeight
// PronunciationTone 是注音与发音替换规则,用 / 分隔,
// 如 "燕少飞/(yan4)(shao3)(fei1)"、"omg/oh my god"。
PronunciationTone []string
// LanguageBoost 增强对指定小语种或方言的识别能力,可设为 "auto" 让模型自行判断。
LanguageBoost string
// SubtitleEnable 启用字幕输出。
SubtitleEnable bool
// ExcludeAggregatedAudio 控制流式结束帧是否携带完整音频。
// 设为 true 时结束帧的音频为空,需由调用方自行拼接分片,可减少尾包体积。
// 仅流式模式生效。
ExcludeAggregatedAudio bool
}
MiniMaxParams 是 MiniMax 非实时 HTTP 合成的参数。
MiniMax 是第三方模型,参数结构与阿里云自研模型完全不同:韵律与音频设置各自 成组,音量取值域是 (0, 10] 而不是 [0, 100],音高是 [-12, 12] 的整数, 且**返回的音频是十六进制编码**而不是 base64。
type MiniMaxResult ¶
type MiniMaxResult struct {
SynthesisResult
// ExtraInfo 是音频元信息(时长、体积、实际采样率等),服务端未返回时为 nil。
ExtraInfo *MiniMaxExtraInfo
// TraceID 是 MiniMax 侧的链路标识,与 RequestID 是两套体系。
TraceID string
}
MiniMaxResult 是一次 MiniMax 合成的结果。
type MiniMaxTimbreWeight ¶
type MiniMaxTimbreWeight struct {
// VoiceID 是音色 ID。必填。
VoiceID string `json:"voice_id"`
// Weight 是权重,取值范围 [1, 100]。必填。
Weight int `json:"weight"`
}
MiniMaxTimbreWeight 是混合音色中的一个成分。
type MiniMaxVoiceSetting ¶
type MiniMaxVoiceSetting struct {
// VoiceID 是音色 ID。使用混合音色时留空并填 TimbreWeights。
VoiceID string
// Speed 是语速,取值范围 [0.5, 2.0],零值表示不设置(服务端默认 1.0)。
Speed float64
// Vol 是音量,取值范围 (0.0, 10.0],零值表示不设置(服务端默认 1.0)。
Vol float64
// Pitch 是音高,取值范围 [-12, 12],服务端默认 0。
// 用指针:0 是合法取值也是默认值,但显式设 0 与不设在语义上一致,
// 这里仍用指针以便与其他可选项保持一致的表达方式。
Pitch *int
// Emotion 是情感,零值表示不设置(模型自动匹配)。
Emotion string
// TextNormalization 启用中英文文本规范化,提升数字阅读场景表现。
TextNormalization bool
// LatexRead 启用 LaTeX 公式朗读。仅支持中文,开启后 LanguageBoost 会被设为 Chinese。
LatexRead bool
}
MiniMaxVoiceSetting 是 MiniMax 的音色与韵律设置。
type Option ¶
type Option func(*config) error
Option 配置 Client。非法取值一律返回错误,不静默回退默认值——静默回退会让 配置写错的调用方在生产环境里得到一个「看起来能跑但行为不对」的客户端。
func WithAudioBuffer ¶
WithAudioBuffer 配置音频帧缓冲容量(帧数),默认 64。
缓冲写满时后台接收会阻塞等待,把反压传导给服务端而不是丢弃音频帧。 调大可以容忍更抖动的消费速率,代价是更高的内存占用上限。
func WithBaseURL ¶
WithBaseURL 用完整的 WebSocket 基址覆盖默认端点,覆盖后地域与专属域名配置不再生效。
接受 ws、wss、http、https 四种 scheme,http 与 https 会被分别改写为 ws 与 wss。 可带路径前缀(如网关场景 wss://gw.example.com/aliyun),协议路径会拼在其后。
func WithCredentialProvider ¶
func WithCredentialProvider(p CredentialProvider) Option
WithCredentialProvider 配置动态凭据鉴权,用于短期密钥、STS 临时凭证或 密钥托管系统轮转等场景。
本库负责缓存与并发去重:凭据在过期前被复用,且并发请求只会触发一次 provider 调用。详见 CredentialProvider。
Example ¶
ExampleWithCredentialProvider 展示动态凭据:适用于短期密钥、STS 临时凭证 或密钥托管系统轮转。本库负责缓存与并发去重。
package main
import (
"context"
"fmt"
"log"
"time"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(
voicecraftali.WithCredentialProvider(func(ctx context.Context) (voicecraftali.Credential, error) {
// 这里换成真实的密钥获取逻辑。
key, expiresAt, err := fetchShortLivedKey(ctx)
if err != nil {
return voicecraftali.Credential{}, err
}
return voicecraftali.Credential{Value: key, ExpiresAt: expiresAt}, nil
}),
// 提前 1 分钟刷新,避开「刚好在传输途中过期」。
voicecraftali.WithCredentialRefresh(time.Minute, 10*time.Second),
)
if err != nil {
log.Fatal(err)
}
fmt.Println(client != nil)
}
func fetchShortLivedKey(context.Context) (string, time.Time, error) {
return "sk-temp", time.Now().Add(time.Hour), nil
}
Output: true
func WithCredentialRefresh ¶
WithCredentialRefresh 配置动态凭据的刷新行为。
skew 是过期提前量:凭据在距过期不足 skew 时被视为已过期并触发刷新, 用于规避「刚好在传输途中过期」导致的偶发 401。 timeout 是单次 provider 调用的时长上限。
默认 skew 与 timeout 均为 30 秒。
func WithDataInspection ¶
func WithDataInspection() Option
WithDataInspection 启用 X-DashScope-DataInspection: enable 请求头, 让服务端对输入输出做内容安全检查。
启用后若文本命中拦截规则,服务端返回 CodeDataInspectionFailed。
func WithDedicatedEndpoint ¶
func WithDedicatedEndpoint() Option
WithDedicatedEndpoint 启用业务空间专属域名({workspaceID}.{region}.maas.aliyuncs.com)。
阿里云建议从共享域名迁移到专属域名以获得更稳定的推理性能。 启用时必须同时配置 WithWorkspaceID,否则 New 返回校验错误。
func WithHTTPClient ¶
WithHTTPClient 提供一个 http.Client,本库从其 Transport 继承代理、 自定义 TLS 配置与自定义拨号函数。Transport 按引用共享,因此连接池、 keep-alive 与 HTTP/2 与调用方的其他请求一并复用。
该 Client 的 Timeout 字段在 WebSocket 与非实时 HTTP 两条路径上都不生效: 它覆盖「连接建立到响应体读完」的全过程,会把正常的 SSE 音频流拦腰截断。 单次调用的超时请用传入各方法的 ctx 控制;WebSocket 的握手与读空闲另由 WithHandshakeTimeout 与 WithReadIdleTimeout 控制。
传入的 http.Client 实例不会被本库修改——Timeout 的清零只作用于内部副本, 因此可以安全地把进程内共享的 http.Client 传进来。
func WithHandshakeTimeout ¶
WithHandshakeTimeout 配置 WebSocket 握手超时,默认 10 秒。
func WithReadIdleTimeout ¶
WithReadIdleTimeout 配置单次读取的空闲超时,默认 60 秒。
每次成功读取后续期。作用是让半开连接(服务端进程消失但 TCP 未收到 FIN) 表现为读超时错误,而不是让读取 goroutine 永久阻塞。
func WithUserAgent ¶
WithUserAgent 配置 User-Agent 请求头,便于服务端追踪调用来源。 默认为 "gtkit-voicecraft-ali/<version>"。
func WithWorkspaceID ¶
WithWorkspaceID 配置阿里云百炼业务空间 ID。
该 ID 会写入 X-DashScope-WorkSpace 请求头;若同时调用 WithDedicatedEndpoint, 还会用于拼出业务空间专属域名。
func WithWriteTimeout ¶
WithWriteTimeout 配置单次写入的超时,默认 10 秒。
type Phoneme ¶
type Phoneme struct {
// Text 是音素文本。
Text string `json:"text"`
// Tone 是音调。文档中该字段为整数(如 2)。
Tone int `json:"tone"`
// BeginTime 是音素起始时间(毫秒)。
BeginTime int `json:"begin_time"`
// EndTime 是音素结束时间(毫秒)。
EndTime int `json:"end_time"`
}
Phoneme 是一个音素的时间戳信息,仅 Sambert 返回。
type Pool ¶
type Pool struct {
// contains filtered or unexported fields
}
Pool 在多个 CosyVoice / Qwen-Audio-TTS 合成任务之间复用 WebSocket 连接。
该 API 允许连接复用:任务收到 task-finished 后, 可以在同一条连接上用新的 task_id 发起下一个任务,省掉 TCP 握手、TLS 握手 与 WebSocket 升级的往返,显著降低首包延迟。
Pool 的入口是 NewSynthesisSession,语义与 Client.NewSynthesisSession 一致。 其余两套 API 直接用 Client 上的对应入口:Sambert 用 Client.NewSambertSession, Qwen-TTS Realtime 用 Client.NewRealtimeSession——后者的服务端在 session.finished 之后会主动关闭连接,本就无法复用。
Pool 从池中取出的会话与 Client 直接创建的会话是同一个 *Session 类型, 用法完全相同:Close 时由 Pool 决定是把连接交回池还是关闭它。
Pool 是并发安全的。
pool := voicecraftali.NewPool(client) defer pool.Close() session, err := pool.NewSynthesisSession(ctx, params)
func NewPool ¶
func NewPool(client *Client, opts ...PoolOption) *Pool
NewPool 创建一个连接池。
client 必须非 nil。PoolOption 与 Client 的 Option 不同,非法取值不生效 而非报错——池的容量与空闲时长只影响性能,写错不会产生错误的合成结果。
Example ¶
ExampleNewPool 展示连接复用:任务之间复用同一条 WebSocket 连接, 省掉握手往返以降低首包延迟。
package main
import (
"context"
"log"
"os"
"time"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
pool := voicecraftali.NewPool(client,
voicecraftali.WithPoolMaxIdle(16),
// 空闲上限需低于服务端 60 秒断连阈值。
voicecraftali.WithPoolIdleTimeout(45*time.Second),
)
defer pool.Close()
ctx := context.Background()
params := &voicecraftali.SynthesisParams{
Model: voicecraftali.ModelCosyVoiceV3Flash,
Voice: "longanyang",
}
for _, text := range []string{"第一句。", "第二句。", "第三句。"} {
session, err := pool.NewSynthesisSession(ctx, params)
if err != nil {
log.Fatal(err)
}
_ = session.SendText(ctx, text)
_ = session.Finish(ctx)
_ = session.Stream(ctx, func([]byte) error { return nil })
// Close 把连接交回池,供下一个任务复用。
_ = session.Close()
}
}
Output:
func (*Pool) Close ¶
Close 关闭池中所有空闲连接。
Close 是幂等的。关闭后 NewSynthesisSession 返回 ErrPoolClosed; 此前已交出、仍在使用中的会话不受影响,它们关闭时连接会被直接关掉 而不是交回池。
func (*Pool) NewSynthesisSession ¶
func (p *Pool) NewSynthesisSession( ctx context.Context, params *SynthesisParams, opts ...SessionOption, ) (*Session, error)
NewSynthesisSession 从池中取一条连接创建 CosyVoice / Qwen-Audio-TTS 会话。
语义与 Client.NewSynthesisSession 完全一致,区别只在于连接可能来自池, 且会话正常结束后连接会被交回池。每个任务都会使用新生成的 task_id—— 复用连接时沿用旧 task_id 会被服务端拒绝。
type PoolOption ¶
type PoolOption func(*Pool)
PoolOption 配置 Pool。
func WithPoolIdleTimeout ¶
func WithPoolIdleTimeout(d time.Duration) PoolOption
WithPoolIdleTimeout 设置空闲连接的存活时长,默认 45 秒。
该值应当明显小于服务端的空闲断连阈值(60 秒),否则会取到已被服务端 关闭的连接。非正数不生效。
func WithPoolMaxIdle ¶
func WithPoolMaxIdle(n int) PoolOption
WithPoolMaxIdle 设置空闲连接数上限,默认 8。 超出上限的连接会被关闭而不是无界堆积。非正数不生效。
type PreviewAudio ¶
type PreviewAudio struct {
// Data 是音频字节(本库已从 base64 解码)。
Data []byte
// SampleRate 是音频的实际采样率。
SampleRate int
// Format 是音频的实际格式。
Format AudioFormat
}
PreviewAudio 是声音设计生成的试听音频。
type PreviewOptions ¶
type PreviewOptions struct {
// SampleRate 是预览音频采样率(Hz),零值表示不设置(服务端默认 24000)。
// Qwen-Audio-TTS / CosyVoice 支持 16000、24000、48000;
// Qwen 另外支持 8000。
SampleRate int
// ResponseFormat 是预览音频格式,零值表示不设置(服务端默认 wav)。
// Qwen-Audio-TTS / CosyVoice 支持 pcm、wav、mp3;Qwen 另外支持 opus。
ResponseFormat AudioFormat
}
PreviewOptions 是声音设计生成的预览音频的编码配置。
type Progress ¶
type Progress struct {
// Type 取 "sentence-begin"(句子开始)、"sentence-synthesis"(音频数据块)
// 或 "sentence-end"(句子结束)。Sambert 不返回该字段。
Type string
// OriginalText 是分句后的句子文本,在 sentence-begin 与 sentence-end 中返回。
// Sambert 不返回该字段。
OriginalText string
// Sentence 是句子与字、音素的时间戳信息,服务端未返回时为 nil。
Sentence *Sentence
}
Progress 是两套 WebSocket 合成 API 在合成过程中返回的句子级进度信息。
通过 WithProgress 注册处理函数接收。开启时间戳参数后 Sentence 中会带有 时间戳数据。Qwen-TTS Realtime 没有等价的句子级进度事件,不会产生 Progress。
两套 API 填充的字段不同:
- CosyVoice / Qwen-Audio-TTS:填 Type 与 OriginalText, Sentence.Index 有效,Words 带 BeginIndex / EndIndex
- Sambert:不填 Type 与 OriginalText, Sentence.BeginTime / EndTime 有效,Words 可带 Phonemes
type Protocol ¶
type Protocol string
Protocol 标识会话使用的 WebSocket 协议。
阿里云百炼的实时语音合成分三套 API,本库把它们收敛到同一个 Session 类型上, Protocol 用于在需要区分时查询当前会话属于哪一套。
const ( // ProtocolSynthesis 是 CosyVoice / Qwen-Audio-TTS 的双向流式协议 // (/api-ws/v1/inference,streaming=duplex)。 ProtocolSynthesis Protocol = "synthesis" // ProtocolSambert 是 Sambert 的单向流式协议 // (/api-ws/v1/inference,streaming=out)。与 ProtocolSynthesis 共用端点, // 但参数集与支持的指令都不同,因此是独立的协议标识。 ProtocolSambert Protocol = "sambert" // ProtocolRealtime 是 Qwen-TTS Realtime 协议(/api-ws/v1/realtime), // 服务 qwen3-tts-*-realtime 与 qwen-tts-realtime 系列。 ProtocolRealtime Protocol = "realtime" )
type QwenTTSParams ¶
type QwenTTSParams struct {
// Model 是模型名,见 ModelQwen3TTSFlash 等常量。必填。
Model string
// Voice 是音色。必填。取值同 Qwen-TTS Realtime 的音色常量。
Voice string
// LanguageType 指定合成音频的语种,零值表示不设置(服务端默认 Auto)。
LanguageType string
// Instructions 是指令控制文本,长度不超过 1600 Token,仅支持中文与英文,
// 仅 qwen3-tts-instruct-flash 系列支持。
Instructions string
// OptimizeInstructions 让服务端对 Instructions 做语义增强与重写。
// 依赖 Instructions 非空。
OptimizeInstructions bool
}
QwenTTSParams 是 Qwen-TTS 非实时 HTTP 合成的参数。
参数集比其他接口小得多:该接口只接受 text、voice、language_type、 instructions 与 optimize_instructions,没有音频格式、采样率、语速等控制项。
type QwenVoice ¶
type QwenVoice struct {
// Voice 是音色名,可直接用作 RealtimeParams.Voice 或 QwenTTSParams.Voice。
Voice string
// TargetModel 是驱动该音色的语音合成模型。
TargetModel string
// FallbackMode 为 true 表示音色是以降级模式创建的:
// 样本音频质量不佳或与文本严重不匹配,复刻效果可能不理想。
FallbackMode bool
// FallbackReason 是降级原因,仅在 FallbackMode 为 true 时有值。
// 可能取值如 no_merged_segments(无法合并音频片段)、
// no_valid_asr_segments(音频与文本严重不匹配)。
FallbackReason string
}
QwenVoice 是一次 Qwen-TTS 声音复刻的结果。
type QwenVoiceAudio ¶
type QwenVoiceAudio struct {
// MIME 是音频的 MIME 类型,取值 audio/wav、audio/mpeg 或 audio/mp4。必填。
MIME string
// Data 是音频的原始字节。必填,且不超过 32 MiB——该上限是本库为
// base64 编码与 JSON 序列化带来的内存放大所设,不是服务端的业务限制。
// 本库会按协议要求把它编码成 Data URL。
Data []byte
}
QwenVoiceAudio 是提交给 Qwen-TTS 声音复刻的样本音频。
type QwenVoiceList ¶
type QwenVoiceList struct {
// Voices 是本页音色。
Voices []QwenVoiceSummary
// PageIndex 是当前页码索引。
PageIndex int
// PageSize 是每页数据条数。
PageSize int
// TotalCount 是音色总数。
TotalCount int
}
QwenVoiceList 是 Qwen-TTS 音色列表的分页结果。
type QwenVoiceSummary ¶
type QwenVoiceSummary struct {
// Voice 是音色名。
Voice string `json:"voice"`
// GmtCreate 是创建时间。
GmtCreate string `json:"gmt_create"`
// GmtModified 是修改时间。
GmtModified string `json:"gmt_modified"`
// Language 是样本音频的语种。
Language string `json:"language"`
// TargetModel 是驱动该音色的语音合成模型。
TargetModel string `json:"target_model"`
}
QwenVoiceSummary 是 Qwen-TTS 音色列表中的一项。
与 VoiceSummary 的差异:音色标识字段叫 voice 而非 voice_id, 带有 language 与 target_model,且**不含审核状态**。
type RealtimeMode ¶
type RealtimeMode string
RealtimeMode 是 Qwen-TTS Realtime 的交互模式。
const ( // ModeServerCommit 服务端自动判断文本分段与合成时机(协议默认)。 // 平衡延迟与质量,适合大段文本连续合成。 ModeServerCommit RealtimeMode = "server_commit" // ModeCommit 客户端调用 Commit 主动触发合成。 // 延迟最低,但需要调用方自行保证句子完整性。 ModeCommit RealtimeMode = "commit" )
type RealtimeParams ¶
type RealtimeParams struct {
// Model 是模型名,见 ModelQwen3TTSFlashRealtime 等常量。必填。
// 该值会作为 URL 查询参数 model 参与连接。
Model string
// Voice 是音色。必填。系统音色见 voice.go 中的常量与官方音色列表。
Voice string
// Mode 是交互模式,零值表示不设置(服务端默认 server_commit)。
Mode RealtimeMode
// LanguageType 指定合成音频的语种,零值表示不设置(服务端默认 Auto)。
// 文本为单一语种时指定具体语种能显著提升合成质量。
LanguageType string
// ResponseFormat 是输出音频格式,零值表示不设置(服务端默认 pcm)。
ResponseFormat AudioFormat
// SampleRate 是输出采样率(Hz),取值 8000、16000、24000 或 48000,
// 零值表示不设置(服务端默认 24000)。
SampleRate int
// SpeechRate 是语速,取值范围 [0.5, 2.0],零值表示不设置(服务端默认 1.0)。
SpeechRate float64
// Volume 是音量,取值范围 [0, 100],服务端默认 50。
// 用指针的原因同 SynthesisParams.Volume。
Volume *int
// PitchRate 是语调,取值范围 [0.5, 2.0],零值表示不设置(服务端默认 1.0)。
PitchRate float64
// BitRate 是码率(kbps),取值范围 [6, 510],**仅 opus 格式有效**,
// 设了别的格式(含 ResponseFormat 留零值,服务端此时默认 pcm)会被拒绝。
// 零值表示不设置(服务端默认 128)。
BitRate int
// Instructions 是指令控制文本,长度不超过 1600 Token,仅支持中文与英文,
// 仅 qwen3-tts-instruct-flash-realtime 系列支持。
Instructions string
// OptimizeInstructions 让服务端对 Instructions 做语义增强与重写。
// 依赖 Instructions 非空,Instructions 为空时不生效。
OptimizeInstructions bool
}
RealtimeParams 是 Qwen-TTS Realtime 的会话参数。
Model 与 Voice 必填,其余留零值即由服务端应用默认值。 qwen-tts-realtime 系列只支持 pcm 格式与 24000 采样率,且不支持 SpeechRate、Volume、PitchRate 与 BitRate;具体以官方 API 参考为准。
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry 持有多个具名 Client,用于在一个进程内同时使用多个 API Key。
Registry 在 NewRegistry 返回后不可变,且可以安全地在多个 goroutine 间共享 (safe for concurrent use):内部映射只读,因此不需要加锁。它不提供构造后 新增或替换条目的方法,与 Client 自身「New 返回后不可变」的契约一致;需要在 单个条目内轮转密钥的场景用 WithCredentialProvider。
用法:
reg, err := voicecraftali.NewRegistry(
[]voicecraftali.Option{voicecraftali.WithUserAgent("svc/1.0")},
voicecraftali.RegistryEntry{
Name: "tenant-a",
Options: []voicecraftali.Option{voicecraftali.WithAPIKey(keyA)},
},
voicecraftali.RegistryEntry{
Name: "tenant-b",
Options: []voicecraftali.Option{
voicecraftali.WithAPIKey(keyB),
voicecraftali.WithWorkspaceID("llm-b"),
voicecraftali.WithDedicatedEndpoint(),
},
},
)
if err != nil {
return err
}
client, err := reg.Client("tenant-b")
func NewRegistry ¶
func NewRegistry(shared []Option, entries ...RegistryEntry) (*Registry, error)
NewRegistry 构造一个 Registry,为每个条目建立一个独立的 Client。
shared 中的选项会应用到每个条目,且在条目自身的选项之前应用,因此条目选项 覆盖同名的共享选项。
本函数不修改 shared 切片,也不写入它的底层数组,因此调用方可以放心传入某个 更大切片的前缀(如 all[:2]),并在 NewRegistry 返回后继续使用该数组。
进程级的公共配置放进 shared 即可,例如共用一个 http.Client 让全部条目复用 同一个连接池,或统一 User-Agent;需要独立连接池的条目在自己的 Options 里 再传一个 WithHTTPClient。
与 New 不同,条目必须通过 WithAPIKey 或 WithCredentialProvider 显式配置凭据, 不会回退到 DASHSCOPE_API_KEY 环境变量:注册表的用途就是区分多个 key,漏配 凭据的条目若静默继承环境里的那一个,它的全部调用会被计入错误的账号。漏配 的条目返回 ErrNoCredential。
以下输入返回错误:条目列表为空、条目名为空、条目名重复、选项为 nil、 条目选项校验失败。 重名是错误而不是「后者覆盖前者」——被覆盖的那个名字会指向另一个 API Key。 任一条目失败即返回 nil 注册表,不返回部分可用的结果;错误信息带上出错的 条目名,不含凭据值,且保留原始错误类型供 errors.Is 与 errors.As 判定。
Example ¶
ExampleNewRegistry 展示一个进程内使用多个 API Key: 两个租户各自一份凭据,其中一个走业务空间专属域名。
package main
import (
"fmt"
"log"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
reg, err := voicecraftali.NewRegistry(
// 共享选项应用到每个条目,条目自身的选项优先。
[]voicecraftali.Option{voicecraftali.WithUserAgent("my-svc/1.0")},
voicecraftali.RegistryEntry{
Name: "tenant-a",
Options: []voicecraftali.Option{voicecraftali.WithAPIKey("sk-a")},
},
voicecraftali.RegistryEntry{
Name: "tenant-b",
Options: []voicecraftali.Option{
voicecraftali.WithAPIKey("sk-b"),
voicecraftali.WithWorkspaceID("llm-b"),
voicecraftali.WithDedicatedEndpoint(),
},
},
)
if err != nil {
log.Fatal(err)
}
client, err := reg.Client("tenant-b")
if err != nil {
log.Fatal(err)
}
fmt.Println(reg.Names(), client != nil)
}
Output: [tenant-a tenant-b] true
func (*Registry) Client ¶
Client 返回 name 对应的 Client。
name 未注册时返回 nil 与包装了 ErrUnknownClient 的错误。
Example ¶
ExampleRegistry_Client 展示取用未注册的名字。
package main
import (
"errors"
"fmt"
"log"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
reg, err := voicecraftali.NewRegistry(nil,
voicecraftali.RegistryEntry{
Name: "tenant-a",
Options: []voicecraftali.Option{voicecraftali.WithAPIKey("sk-a")},
},
)
if err != nil {
log.Fatal(err)
}
_, err = reg.Client("tenant-z")
fmt.Println(errors.Is(err, voicecraftali.ErrUnknownClient))
}
Output: true
type RegistryEntry ¶
RegistryEntry 是注册表中的一个条目:一个逻辑名加一组构造选项。
Options 就是 New 接受的那套 Option,因此每个条目都能独立配置凭据、业务空间、 专属域名与自定义基址——同一地域下不同 API Key 的端点差异正来自后三者。
Name 是调用方自定的逻辑名(租户名、业务线名等),只用于索引,不参与任何 请求内容。
type SambertParams ¶
type SambertParams struct {
// Model 是模型名,如 ModelSambertZhichu。必填。音色由模型名决定。
Model string
// Text 是待合成的全文。必填——Sambert 不支持增量送文本。
Text string
// Format 是输出音频格式,取值 pcm、wav 或 mp3(不支持 opus),
// 零值表示不设置(服务端默认 wav)。
Format AudioFormat
// SampleRate 是输出采样率(Hz),取值 8000、16000、22050 或 24000,
// 零值表示不设置(服务端默认 16000)。
SampleRate int
// Volume 是音量,取值范围 [0, 100],服务端默认 50。
// 用指针的原因同 SynthesisParams.Volume。
Volume *int
// Rate 是语速,取值范围 [0.5, 2.0],服务端默认 1.0。零值表示不设置。
Rate float64
// Pitch 是语调,取值范围 [0.5, 2.0],服务端默认 1.0。零值表示不设置。
Pitch float64
// WordTimestampEnabled 启用字级别时间戳,所有 Sambert 模型均支持。
WordTimestampEnabled bool
// PhonemeTimestampEnabled 启用音素级别时间戳。
// 需要同时开启 WordTimestampEnabled,否则本库拒绝该组合。
PhonemeTimestampEnabled bool
}
SambertParams 是 Sambert(WebSocket 单向流式)的合成参数。
Sambert 与 CosyVoice / Qwen-Audio-TTS 是两套不同的 API,参数集不是子集关系:
- **没有 Voice**:音色由模型名决定(如 sambert-zhichu-v1 是知厨)
- Text 必填:全文在建立会话时随 run-task 一次性提交,不支持增量送文本
- 支持 PhonemeTimestampEnabled,而 API 1 没有该参数
- 没有 BitRate、Seed、LanguageHints、Instruction、EnableSSML、HotFix 与 AIGC 系列参数
Sambert 仅在北京地域可用,且是早期模型;新项目建议优先选用 CosyVoice 或 Qwen-Audio-TTS。
type Sentence ¶
type Sentence struct {
// Index 是句子编号,从 0 开始(CosyVoice / Qwen-Audio-TTS)。
Index int `json:"index"`
// BeginTime 是句子起始时间(毫秒,Sambert)。
BeginTime int `json:"begin_time"`
// EndTime 是句子结束时间(毫秒,Sambert)。
EndTime int `json:"end_time"`
// Words 是字级时间戳列表,需开启对应参数才有内容。
Words []Word `json:"words"`
}
Sentence 是一个句子的进度与时间戳信息。
字段按所属 API 填充:Index 由 CosyVoice / Qwen-Audio-TTS 返回, BeginTime 与 EndTime 由 Sambert 返回,未返回的字段保持零值。
type Session ¶
type Session struct {
// contains filtered or unexported fields
}
Session 是一次实时语音合成会话。
三套 WebSocket API(CosyVoice/Qwen-Audio-TTS、Sambert、Qwen-TTS Realtime) 共用本类型,方法集合与语义一致,因此切换 API 不需要改动读写代码。 各 API 确实存在的能力差异以 ErrUnsupportedOperation 显式暴露,不静默降级:
| 方法 | CosyVoice/Qwen-Audio-TTS | Sambert | Qwen-TTS Realtime | |---------------|--------------------------|--------------------------|--------------------------| | SendText | 支持 | ErrUnsupportedOperation | 支持 | | Commit | ErrUnsupportedOperation | ErrUnsupportedOperation | 支持 | | Flush | 支持 | ErrUnsupportedOperation | ErrUnsupportedOperation | | Finish | 发送 finish-task | 空操作(该 API 无此指令) | 发送 session.finish | | Cancel | 支持 | ErrUnsupportedOperation | ErrUnsupportedOperation | | CancelResponse| ErrUnsupportedOperation | ErrUnsupportedOperation | 支持 |
Cancel 与 CancelResponse 的区别是终结性:Cancel 结束整个任务,之后不能再 SendText;CancelResponse 只取消当前响应,会话继续可用。
并发契约(safe for concurrent use,但含义有限):
- 一个 goroutine 写(SendText / Commit / Flush / Finish / Cancel / CancelResponse)与另一个 goroutine 读(Read / ReadContext / Stream)可以并发进行。
- 多个 goroutine 并发写是安全的,写入会被串行化。
- 多个 goroutine 并发读会各自取走不同的音频帧,通常不是想要的效果; 音频应当由单个 goroutine 消费。
- Close 可以在任意时刻、由任意 goroutine 并发调用,且是幂等的。
注意:串行化只保证安全,不提升吞吐——一个会话就是一条 WebSocket 连接。 需要并发合成请创建多个会话。
使用流程:
session, err := client.NewSynthesisSession(ctx, params)
if err != nil {
return err
}
defer session.Close()
go func() {
_ = session.SendText(ctx, "床前明月光,疑是地上霜。")
_ = session.Finish(ctx)
}()
err = session.Stream(ctx, func(audio []byte) error {
_, err := w.Write(audio)
return err
})
调用方必须持续读取音频:音频缓冲写满后接收会阻塞,反压传导到服务端, 最终会触发服务端的空闲断连。
func (*Session) Cancel ¶
Cancel 中断当前轮次合成。
仅 CosyVoice / Qwen-Audio-TTS 支持,且服务端侧的支持范围有限: 北京地域 Qwen-Audio-TTS 全系支持、CosyVoice 仅 v2 及以上支持; 新加坡地域 CosyVoice 不支持。 服务端会立即结束当前任务并返回结束事件。
Sambert 与 Qwen-TTS Realtime 返回 ErrUnsupportedOperation:前者不接受 finish-task 指令(取消依赖它),后者没有终结式取消——要取消当前响应而保留 会话,用 CancelResponse。
Cancel 与 Finish 一样是终结动作:调用后不能再 SendText, 仍应继续读取直到音频流结束。
func (*Session) CancelResponse ¶
CancelResponse 请求服务端取消当前响应的合成。
仅 Qwen-TTS Realtime 支持(发出 response.cancel 事件);CosyVoice / Qwen-Audio-TTS 与 Sambert 返回 ErrUnsupportedOperation——前者的取消是终结式的, 见 Cancel。
CancelResponse 不是终结动作:它不把会话标记为已结束,调用后 SendText 不会 因此返回 ErrSessionFinished。服务端在收到该事件之后是否仍接受新文本由服务端 决定,本方法只负责把事件发出去。
Example ¶
ExampleSession_CancelResponse 展示取消 Qwen-TTS Realtime 的当前响应。
CancelResponse 不终结会话:取消掉当前这段音频之后,还能继续送新的文本。 需要结束整个任务用 Finish。
package main
import (
"context"
"log"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New()
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
session, err := client.NewRealtimeSession(ctx, &voicecraftali.RealtimeParams{
Model: voicecraftali.ModelQwen3TTSFlashRealtime,
Voice: voicecraftali.VoiceCherry,
})
if err != nil {
log.Fatal(err)
}
defer session.Close()
if err := session.SendText(ctx, "这段不要了。"); err != nil {
log.Fatal(err)
}
// 用户改主意了:取消当前响应,会话继续可用。
if err := session.CancelResponse(ctx); err != nil {
log.Fatal(err)
}
if err := session.SendText(ctx, "换成这段。"); err != nil {
log.Fatal(err)
}
if err := session.Finish(ctx); err != nil {
log.Fatal(err)
}
}
Output:
func (*Session) Close ¶
Close 关闭会话并释放资源。
Close 是幂等的,可由任意 goroutine 在任意时刻调用。返回时保证: 接收 goroutine 已退出,底层连接已关闭或已交回连接池,阻塞在 Read / ReadContext / Stream 上的调用方已被解除阻塞。
会话属于连接池且任务干净结束时,连接被交回池而不是关闭。
func (*Session) Commit ¶
Commit 把当前文本缓冲区立即提交合成。
仅 Qwen-TTS Realtime 支持。commit 模式下必须调用 Commit 才会触发合成; server_commit 模式下服务端自动判定时机,调用 Commit 表示立即合成已缓冲的文本。
两套 WebSocket 合成 API(CosyVoice / Qwen-Audio-TTS 与 Sambert)返回 ErrUnsupportedOperation:它们没有独立的提交动作。
func (*Session) Finish ¶
Finish 通知服务端文本已全部送完。
调用后不能再 SendText,但仍应继续 Read 或 Stream 直到音频流结束—— 服务端会在 Finish 之后继续返回剩余音频。
Finish 是幂等的:重复调用返回 nil 且不会重复发帧。
func (*Session) Flush ¶
Flush 让服务端立即合成已送入但尚未合成的文本,不结束任务。
仅 CosyVoice / Qwen-Audio-TTS 支持;Sambert 与 Qwen-TTS Realtime 返回 ErrUnsupportedOperation。后者用 Commit 达到同样目的。
Flush 不是终结动作:调用后 SendText、Finish 与再次 Flush 都仍然可用。 该指令不携带文本,因此既不计入 SendText 的累计字符额度,也不占用 SSML 模式下 「整个任务只允许送入一次文本」的名额。
Example ¶
ExampleSession_Flush 展示用 Flush 压低首包延迟。
Flush 让服务端立即合成已送入但尚未合成的文本,不结束任务,因此调用后仍可 继续送文本。适合上游文本逐字到达(如 LLM 流式输出)、又想尽快听到声音的场景。
package main
import (
"context"
"log"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New()
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
session, err := client.NewSynthesisSession(ctx, &voicecraftali.SynthesisParams{
Model: voicecraftali.ModelCosyVoiceV3Flash,
Voice: "longanyang",
})
if err != nil {
log.Fatal(err)
}
defer session.Close()
go func() {
// 攒到一个完整句子就 Flush,不必等整段文本都送完。
for _, line := range []string{"床前明月光,", "疑是地上霜。"} {
if err := session.SendText(ctx, line); err != nil {
log.Print(err)
return
}
if err := session.Flush(ctx); err != nil {
log.Print(err)
return
}
}
if err := session.Finish(ctx); err != nil {
log.Print(err)
}
}()
if err := session.Stream(ctx, func(audio []byte) error {
_ = audio
return nil
}); err != nil {
log.Fatal(err)
}
}
Output:
func (*Session) ID ¶
ID 返回服务端维度的会话标识:两套 WebSocket 合成 API 为 task_id, Qwen-TTS Realtime 为 session id。 排查问题、提交工单时需要提供该值。
func (*Session) Read ¶
Read 读取下一帧音频。
这是拉模式接口,适合需要精细控制处理节奏的场景。
返回值:
- (data, nil):成功读到一帧音频
- (nil, io.EOF):音频流正常结束
- (nil, err):服务端返回失败事件、连接异常或读超时
音频流因错误终止后,重复调用仍返回同一个错误,不会退化为 io.EOF。
Read 在等待时不响应 context 取消,需要该能力请用 ReadContext。
func (*Session) ReadContext ¶
ReadContext 与 Read 相同,但在等待音频时响应 ctx 取消。
func (*Session) SendText ¶
SendText 增量送入一段待合成文本,可多次调用。
文本送完后调用 Finish 通知服务端。
返回 ErrSessionFinished 表示已调用过 Finish;返回 ErrSessionClosed 表示会话已关闭; 返回 ErrTextTooLong 表示超出协议的单次或累计长度上限; 返回 ErrUnsupportedOperation 表示该 API 不支持增量送文本(Sambert 的全文 在创建会话时随 SambertParams.Text 一次提交)。
type SessionOption ¶
type SessionOption func(*sessionConfig)
SessionOption 配置单个会话。
func WithProgress ¶
func WithProgress(fn func(Progress)) SessionOption
WithProgress 注册合成进度回调,用于接收两套 WebSocket 合成 API (CosyVoice / Qwen-Audio-TTS 与 Sambert)的句子级进度与时间戳。
回调在会话的接收 goroutine 上同步执行,与音频投递共用同一个 goroutine: 回调阻塞会同时挡住音频投递,因此其中不要做耗时操作,需要时自行转交给 别的 goroutine。Qwen-TTS Realtime 没有等价事件,不会触发该回调。
时间戳数据需要同时开启 SynthesisParams.WordTimestampEnabled 或 PhonemeTimestampEnabled 才会返回。
Example ¶
ExampleWithProgress 展示接收句子级进度与字级时间戳。
package main
import (
"context"
"log"
"os"
voicecraftali "github.com/gtkit/go-voicecraft-ali"
)
func main() {
client, err := voicecraftali.New(voicecraftali.WithAPIKey(os.Getenv("DASHSCOPE_API_KEY")))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
session, err := client.NewSynthesisSession(ctx, &voicecraftali.SynthesisParams{
Model: voicecraftali.ModelCosyVoiceV3Flash,
Voice: "longanyang",
WordTimestampEnabled: true,
}, voicecraftali.WithProgress(func(p voicecraftali.Progress) {
// 回调在接收 goroutine 上同步执行,不要做耗时操作。
if p.Type == "sentence-end" && p.Sentence != nil {
for _, w := range p.Sentence.Words {
log.Printf("%q %d-%dms", w.Text, w.BeginTime, w.EndTime)
}
}
}))
if err != nil {
log.Fatal(err)
}
defer session.Close()
_ = session.SendText(ctx, "床前明月光")
_ = session.Finish(ctx)
_ = session.Stream(ctx, func([]byte) error { return nil })
}
Output:
type SynthesisParams ¶
type SynthesisParams struct {
// Model 是模型名,见 ModelQwenAudioTTSFlash 等常量。必填。
Model string
// Voice 是音色。必填。系统音色见 voice.go 中的常量与官方音色列表,
// 复刻/设计音色直接传其 ID。
Voice string
// Format 是输出音频格式,零值表示不设置。
//
// 不设置时服务端返回的是**裸 PCM**(16 bit 单声道,采样率随 SampleRate),
// 不是 mp3——实测:同一段文本、cosyvoice-v3-flash,不传 Format 得到 87360 字节、
// 头部 00 00 00 00;显式传 FormatMP3 得到 37531 字节、头部 49 44 33(ID3)。
// 裸 PCM 没有容器头,调用方无法从数据本身得知采样率,也不能直接交给播放器。
// 需要可直接播放的音频请显式指定 FormatMP3。
Format AudioFormat
// SampleRate 是输出采样率(Hz),取值 8000、16000、22050、24000、44100 或 48000,
// 零值表示不设置(服务端默认 22050)。
SampleRate int
// Volume 是音量,取值范围 [0, 100],服务端默认 50。
//
// 用指针而非 int:音量 0 是合法取值,值语义无法把它与「不设置」区分开,
// 会把「要求静音」误当成「用默认 50」。用 new(0) 明确表达前者。
Volume *int
// Rate 是语速,取值范围 [0.5, 2.0],服务端默认 1.0。零值表示不设置。
// 对应线格式字段 rate(官方 SDK 中名为 speech_rate)。
Rate float64
// Pitch 是语调,取值范围 [0.5, 2.0],服务端默认 1.0。零值表示不设置。
// 对应线格式字段 pitch(官方 SDK 中名为 pitch_rate)。
Pitch float64
// BitRate 是音频码率(kbps),取值范围 [6, 510],**仅 opus 格式有效**,
// 设了别的格式(含 Format 留零值,此时服务端返回裸 PCM,同样不是 opus)会被拒绝。
// 零值表示不设置(服务端默认 32)。cosyvoice-v1 不支持该参数。
BitRate int
// Seed 是随机数种子,取值范围 [0, 65535]。在模型版本、文本、音色及其他
// 参数相同时,相同 Seed 可复现相同结果。零值即服务端默认值 0。
// cosyvoice-v1 不支持该参数。
Seed int
// EnableSSML 启用 SSML 解析,此时 Text 需为 SSML 标记文本。
//
// 仅 CosyVoice 系列的部分模型支持(见 SupportsSSML),且音色需为复刻音色
// 或官方列表中标注支持 SSML 的系统音色。用不支持的模型启用会在建立连接
// 之前被拒绝。
//
// 启用后整个任务**只允许送入一次文本**:第二次 SendText 返回
// ErrSSMLSingleSend,不会发往服务端。
//
// 拼接 SSML 时外部文本需先过 EscapeSSMLText 转义。
EnableSSML bool
// WordTimestampEnabled 启用字级别时间戳,时间戳随进度事件返回,
// 通过 WithProgress 注册的回调接收。
WordTimestampEnabled bool
// EnableMarkdownFilter 在合成前过滤文本中的 Markdown 标记符号。
// 仅 cosyvoice-v3-flash 的复刻音色支持。
EnableMarkdownFilter bool
// LanguageHints 指定目标语言以提升合成效果,取值为 zh、en、fr、de、ja、ko、
// ru、pt、th、id、vi、es、it、ms、fil、ar。
// 服务端当前只处理第一个元素,建议只传一个值。cosyvoice-v1 不支持该功能。
LanguageHints []string
// Instruction 是自然语言指令,用于控制方言、情感或角色等合成效果。
Instruction string
// EnableAIGCTag 在生成的音频中嵌入 AIGC 隐性标识(支持 wav/mp3/opus)。
EnableAIGCTag bool
// AIGCPropagator 设置 AIGC 隐性标识的 ContentPropagator 字段,
// 仅在 EnableAIGCTag 为 true 时生效。
AIGCPropagator string
// AIGCPropagateID 设置 AIGC 隐性标识的 PropagateID 字段,
// 仅在 EnableAIGCTag 为 true 时生效。
AIGCPropagateID string
// HotFix 是文本热修复配置。cosyvoice-v2 与 cosyvoice-v1 不支持。
HotFix *HotFix
}
SynthesisParams 是 CosyVoice / Qwen-Audio-TTS(WebSocket 双向流式)的合成参数。
Model 与 Voice 必填,其余留零值即由服务端应用默认值。 各模型支持的参数子集不同(例如 cosyvoice-v1 不支持 Opus 格式、BitRate 与 Seed, cosyvoice-v2 与 v1 不支持 HotFix),具体以官方 API 参考为准; 本库只做取值域校验,不做模型能力矩阵校验。
Sambert 不使用本类型——它的参数集不同,见 SambertParams。
type SynthesisResult ¶
type SynthesisResult struct {
// RequestID 是本次调用的唯一标识符,排查问题时需要提供。
RequestID string
// Audio 是合成出的音频。
Audio AudioRef
// Usage 是本次调用的计费信息,服务端未返回时为 nil。
Usage *Usage
}
SynthesisResult 是一次非实时 HTTP 合成的结果。
type TaskFailedError ¶
type TaskFailedError struct {
// TaskID 是本次任务的 task_id,提交工单排查时需要提供。
TaskID string
// Code 取服务端 header.error_code。可传给 CodeDescription 获取中文说明。
Code Code
// Message 取服务端 header.error_message,是服务端原始文案。
Message string
}
TaskFailedError 表示两套 WebSocket 合成 API(CosyVoice / Qwen-Audio-TTS 与 Sambert)的服务端返回了 task-failed 事件。
服务端在发出该事件后会关闭连接,且该连接不可复用。
func (*TaskFailedError) Error ¶
func (e *TaskFailedError) Error() string
type Usage ¶
type Usage struct {
// Characters 是计费字符数。
Characters int `json:"characters,omitempty"`
// TotalTokens 是输入与输出的总 Token 数。
TotalTokens int `json:"total_tokens,omitempty"`
// InputTokens 是输入 Token 数。
InputTokens int `json:"input_tokens,omitempty"`
// OutputTokens 是输出 Token 数。
OutputTokens int `json:"output_tokens,omitempty"`
}
Usage 是服务端返回的计费信息。
各 API 的计费口径不同,未采用的口径保持零值:
- CosyVoice / Qwen-Audio-TTS / Sambert / Qwen3-TTS Realtime:按字符计费(Characters)
- Qwen-TTS Realtime:按 Token 计费(TotalTokens 等); 音频按每秒 50 Token 计,不足 1 秒按 50 Token 计
type ValidationError ¶
type ValidationError struct {
// Field 是出错的字段名,取协议线格式中的名字(如 "voice"、"sample_rate"),
// 便于与官方文档对照。
Field string
// Reason 说明为什么该取值不合法。
Reason string
}
ValidationError 表示一次本地参数校验失败,指明出错字段与原因。
本库在建立任何连接、发送任何帧之前完成校验,因此拿到 *ValidationError 意味着请求从未离开进程。
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string
func (*ValidationError) Is ¶
func (e *ValidationError) Is(target error) bool
Is 让 *ValidationError 能被 errors.Is(err, ErrInvalidParam) 命中。
type VoiceDetail ¶
type VoiceDetail struct {
// ResourceLink 是复刻所用音频文件的地址,仅声音复刻的音色有值。
ResourceLink string `json:"resource_link"`
// TargetModel 是驱动该音色的语音合成模型。
TargetModel string `json:"target_model"`
// GmtCreate 是创建时间。
GmtCreate string `json:"gmt_create"`
// GmtModified 是修改时间。
GmtModified string `json:"gmt_modified"`
// Status 是审核状态。
Status VoiceStatus `json:"status"`
// VoicePrompt 是声音描述文本,仅声音设计的音色有值。
VoicePrompt string `json:"voice_prompt"`
// PreviewText 是预览文本,仅声音设计的音色有值。
PreviewText string `json:"preview_text"`
}
VoiceDetail 是音色详情。
声音复刻与声音设计的音色共用本结构:复刻的音色有 ResourceLink(样本音频地址), 设计的音色有 VoicePrompt 与 PreviewText,两组字段互斥。
func (*VoiceDetail) Designed ¶
func (v *VoiceDetail) Designed() bool
Designed 判断该音色是声音设计产出的(而非声音复刻)。
type VoiceStatus ¶
type VoiceStatus string
VoiceStatus 是复刻音色的审核状态。
该状态体系仅适用于 Qwen-Audio-TTS / CosyVoice 的复刻音色; Qwen-TTS 的音色查询结果不含状态字段。
const ( // VoiceStatusDeploying 审核中或处理中。 VoiceStatusDeploying VoiceStatus = "DEPLOYING" // VoiceStatusOK 审核通过,可正常用于语音合成。 VoiceStatusOK VoiceStatus = "OK" // VoiceStatusUndeployed 审核未通过,不可使用。 VoiceStatusUndeployed VoiceStatus = "UNDEPLOYED" )
func (VoiceStatus) Usable ¶
func (s VoiceStatus) Usable() bool
Usable 判断该音色能否用于语音合成。
用复刻音色合成却听不到声音时,第一步就该确认状态是不是 OK。
type VoiceSummary ¶
type VoiceSummary struct {
// VoiceID 是音色 ID,可直接用作合成参数中的 Voice。
VoiceID string `json:"voice_id"`
// GmtCreate 是创建时间,形如 "2024-12-11 13:38:02"。
GmtCreate string `json:"gmt_create"`
// GmtModified 是修改时间。
GmtModified string `json:"gmt_modified"`
// Status 是审核状态,见 VoiceStatus。
Status VoiceStatus `json:"status"`
// VoicePrompt 是声音描述文本,仅声音设计的音色有值。
VoicePrompt string `json:"voice_prompt"`
// PreviewText 是预览文本,仅声音设计的音色有值。
PreviewText string `json:"preview_text"`
}
VoiceSummary 是音色列表中的一项。
声音复刻与声音设计产出的音色共用同一份列表:VoicePrompt 与 PreviewText 只有声音设计的音色才有值,复刻出来的音色这两个字段为空。
func (*VoiceSummary) Designed ¶
func (v *VoiceSummary) Designed() bool
Designed 判断该音色是声音设计产出的(而非声音复刻)。
type Word ¶
type Word struct {
// Text 是字的文本内容。
Text string `json:"text"`
// BeginIndex 是字在句子中的开始位置索引,从 0 开始(CosyVoice / Qwen-Audio-TTS)。
BeginIndex int `json:"begin_index"`
// EndIndex 是字在句子中的结束位置索引,从 1 开始(CosyVoice / Qwen-Audio-TTS)。
EndIndex int `json:"end_index"`
// BeginTime 是字对应音频的起始时间(毫秒)。
BeginTime int `json:"begin_time"`
// EndTime 是字对应音频的结束时间(毫秒)。
EndTime int `json:"end_time"`
// Phonemes 是音素级时间戳,仅 Sambert 在开启 PhonemeTimestampEnabled 后返回。
Phonemes []Phoneme `json:"phonemes,omitempty"`
}
Word 是一个字的时间戳信息。
BeginIndex 与 EndIndex 由 CosyVoice / Qwen-Audio-TTS 返回; Phonemes 由 Sambert 在开启音素级时间戳后返回。未返回的字段保持零值。
Source Files
¶
- capability.go
- client.go
- codes.go
- constraints.go
- credential.go
- doc.go
- endpoint.go
- errors.go
- http.go
- http_cosyvoice.go
- http_minimax.go
- http_qwen.go
- models.go
- options.go
- realtime_cosyvoice.go
- realtime_driver.go
- realtime_pool.go
- realtime_qwen.go
- realtime_sambert.go
- realtime_session.go
- realtime_synthesize.go
- realtime_task.go
- realtime_wire.go
- registry.go
- result.go
- ssml.go
- transport_http.go
- uuid.go
- version.go
- voice.go
- voiceclone.go
- voiceclone_minimax.go
- voiceclone_qwen.go
- voicedesign.go
- voicedesign_cosyvoice.go
- voicedesign_qwen.go