本トピックでは、WebSocket 接続を用いた CosyVoice 音声合成サービスへのアクセス方法について説明します。
DashScope SDK は Java および Python のみをサポートしています。その他のプログラミング言語で CosyVoice 音声合成アプリケーションを構築する場合は、WebSocket 接続を用いてサービスと通信してください。
ユーザーガイド:モデルの概要および選択に関する推奨事項については、「リアルタイム音声合成 — CosyVoice」をご参照ください。
WebSocket は、全二重通信をサポートするネットワークプロトコルです。クライアントとサーバーは単一のハンドシェイクにより持続的接続を確立し、双方が相互にデータを能動的にプッシュできます。これにより、リアルタイム性能および効率性において顕著な利点が得られます。
一般的なプログラミング言語向けには、以下のような多数の利用可能な WebSocket ライブラリおよびサンプルが提供されています。
-
Go:
gorilla/websocket -
PHP:
Ratchet -
Node.js:
ws
開発を開始する前に、WebSocket の基本原理および技術的詳細について十分に理解してください。
CosyVoice モデルは WebSocket 接続のみをサポートしており、HTTP REST API はサポートしていません。 HTTP リクエスト(例:POST)を用いてサービスを呼び出した場合、サービスから InvalidParameter エラーまたは URL エラーが返されます。
前提条件
API キーを取得済みです。
モデルおよび課金
「リアルタイム音声合成 — CosyVoice」をご参照ください。
音声合成テキストの文字数制限およびフォーマット規則
テキスト長の制限
各 continue-task 命令 には、最大 20,000 文字まで含めることができます。すべての continue-task 命令 の呼び出しにおいて、送信される文字数の合計は 200,000 を超えてはなりません。
文字数カウント規則
中国語文字(簡体字・繁体字、日本語漢字、韓国語漢字を含む)は 2 文字としてカウントされます。句読点、英数字、日本語/韓国語の仮名/ハングルなどその他のすべての文字は 1 文字としてカウントされます。
SSML タグはテキスト長の計算対象には含まれません。
例:
"你好"→ 2(中国語文字)+ 2(中国語文字)= 4 文字"中A文123"→ 2(中国語文字)+ 1(A)+ 2(中国語文字)+ 1(1)+ 1(2)+ 1(3)= 8 文字"中文。"→ 2(中国語文字)+ 2(中国語文字)+ 1(.)= 5 文字"中 文。"→ 2(中国語文字)+ 1(半角スペース)+ 2(中国語文字)+ 1(.)= 6 文字"<speak>你好</speak>"→ 2(中国語文字)+ 2(中国語文字)= 4 文字
エンコーディング形式
UTF-8 エンコーディングを使用してください。
数式のサポート
数式のパース機能は、cosyvoice-v3.5-flash、cosyvoice-v3.5-plus、cosyvoice-v3-flash、cosyvoice-v3-plus、cosyvoice-v2 のみで利用可能です。小学校および中学校レベルの基本的な演算、代数、幾何学などの一般的な数式をサポートしています。
この機能は中国語のみをサポートします。
「LaTeX 数式を音声に変換(中国語のみ)」をご参照ください。
SSML マークアップ言語のサポート
SSML を使用するには、以下のすべての条件を満たす必要があります。
モデルのサポート: SSML をサポートするモデルは、cosyvoice-v3.5-flash、cosyvoice-v3.5-plus、cosyvoice-v3-flash、cosyvoice-v3-plus、cosyvoice-v2 のみです。
音声のサポート: SSML 対応音声を使用する必要があります。サポートされる音声は以下のとおりです。
すべてのクローン音声(音声クローン API を用いて作成されたカスタム音声)。
音声一覧で SSML 対応とマークされたシステム音声。
説明SSML をサポートしないシステム音声(一部の基本音声など)を使用した場合、
enable_ssmlパラメーターを有効化すると「SSML text is not supported at the moment!(現在、SSML テキストはサポートされていません!)」というエラーが返されます。パラメーターの設定: run-task 命令 内で、
enable_ssmlパラメーターをtrueに設定します。
すべての条件を満たした後、continue-task 命令 を用いて SSML 形式のテキストを送信します。完全な例については、「クイックスタート」をご参照ください。
インタラクションフロー
クライアントからサーバーへ送信されるメッセージは 命令 と呼ばれます。サーバーはクライアントに対して、JSON 形式の イベント およびバイナリ音声ストリームの 2 種類のメッセージを返します。
時系列順に、クライアントとサーバー間のインタラクションフローは以下のとおりです。
接続の確立:クライアントがサーバーとの WebSocket 接続を確立します。
タスクの開始:クライアントが run-task 命令 を送信してタスクを開始します。
確認の待機:クライアントがサーバーから task-started イベント を受信し、タスクが正常に開始されたことを確認します。その後、以降のステップを実行できます。
合成対象テキストの送信:
クライアントは、合成対象テキストを含む 1 つ以上の continue-task 命令 を順次送信します。サーバーは完全な文を受信すると、result-generated イベント および音声ストリームを返します。(テキスト長の制約が適用されます。「continue-task 命令」内の
textフィールドの説明をご参照ください。)説明複数の continue-task 命令 を送信し、テキストフラグメントを順序立てて提出できます。サーバーは自動的にそれらを文に分割します。
完全な文は即座に合成され、クライアントはすぐに音声を受信します。
不完全な文はキャッシュされ、完全になるまで合成されません。不完全な文については音声は返されません。
finish-task 命令 を送信すると、サーバーはキャッシュされたすべてのコンテンツを強制的に合成します。
音声の受信:
binaryチャンネルを介して音声ストリームを受信します。タスク終了の通知:
すべてのテキストを送信した後、クライアントは finish-task 命令 を送信し、サーバーにタスク終了を通知します。その後もサーバーからの音声を継続して受信してください。このステップをスキップしないでください。そうしないと、音声や音声ストリームの終端を欠落させる可能性があります。
タスクの終了:
クライアントがサーバーから task-finished イベント を受信すると、タスクが終了したことを示します。
接続の閉じる:クライアントが WebSocket 接続を閉じます。
リソース使用効率を向上させるため、各タスクごとに新しい接続を開くのではなく、単一の WebSocket 接続を複数のタスクで再利用してください。「接続オーバーヘッドおよび再利用」をご参照ください。
task_id は一貫して維持する必要があります。1 つの合成タスクにおいて、run-task、すべての continue-task、および finish-task 命令 は同じ task_id を使用しなければなりません。
エラーによる影響: 異なる task_id を使用すると、以下の問題が発生します。
サーバーがリクエストを関連付けられず、音声ストリームの配信が乱れる。
テキストコンテンツが誤ったタスクに割り当てられ、音声の整合性が損なわれる。
タスクのステータスが異常になり、task-finished イベントが欠落する可能性がある。
課金が失敗し、使用量統計が不正確になる。
正しい方法:
run-task を送信する際に、一意の task_id(UUID を使用するなど)を生成します。
その task_id を変数に格納します。
その後のすべての continue-task および finish-task 命令で、同一の task_id を使用します。
タスクが終了した後(task-finished を受信した後)、新しいタスクでは新しい task_id を生成します。
クライアント実装に関する注意事項
WebSocket クライアント(特に Flutter、Web、モバイルプラットフォーム上)を実装する際は、サーバーとクライアントの責任範囲を明確に定義し、完全かつ安定した音声合成タスクを保証してください。
サーバーおよびクライアントの責任
サーバーの責任
サーバーは、順序通りに完全な音声ストリームを返すことを保証します。音声データの順序や完全性を心配する必要はありません。サーバーは、テキストの順序に従ってすべての音声チャンクを生成・プッシュします。
クライアントの責任
クライアントは、以下の重要なタスクを処理する必要があります。
すべての音声チャンクの読み取りおよび連結
サーバーは音声を複数のバイナリフレームとして送信します。クライアントはすべてのチャンクを受信し、最終的な音声ファイルを形成するために連結する必要があります。サンプルコードを以下に示します。
# Python の例:音声チャンクの連結 with open("output.mp3", "ab") as f: # 追加モード f.write(audio_chunk) # audio_chunk は各受信したバイナリ音声チャンク// JavaScript の例:音声チャンクの連結 const audioChunks = []; ws.onmessage = (event) => { if (event.data instanceof Blob) { audioChunks.push(event.data); // すべての音声チャンクを収集 } }; // タスク完了後に音声をマージ const audioBlob = new Blob(audioChunks, { type: 'audio/mp3' });WebSocket のライフサイクル全体の管理
音声合成タスクの全体を通して——run-task 命令 の送信から task-finished イベント の受信まで——WebSocket を早期に切断しないでください。よくある誤りを以下に示します。
すべての音声チャンクが到着する前に接続を閉じ、音声が不完全になる。
finish-task 命令 を送信し忘れ、キャッシュされたテキストが未処理のままになる。
ページ遷移やアプリがバックグラウンドに移行した際に、WebSocket のキープアライブを適切に処理できない。
重要モバイルアプリ(Flutter、iOS、Android)では、バックグラウンドへの移行時にネットワーク接続を慎重に管理する必要があります。バックグラウンドタスクまたはフォアグラウンドサービス内で WebSocket を維持するか、フォアグラウンド復帰時にタスク状態を確認して再接続してください。
ASR→LLM→TTS ワークフローにおけるテキストの整合性
ASR→LLM→TTS ワークフローでは、TTS に渡すテキストが途中で途切れないよう、完全な状態であることを保証してください。具体的には以下のようにします。
LLM が完全な文または段落を生成するのを待ってから、continue-task 命令 を送信します。文字単位でのストリーミング送信は避けてください。
ストリーミング合成が必要な場合(再生中に生成する場合)、ピリオドや疑問符などの自然な文の区切りでテキストを送信します。
LLM の出力が完了したら、必ず finish-task 命令 を送信し、末尾のコンテンツを欠落させないようにしてください。
プラットフォーム固有のヒント
Flutter:
web_socket_channelパッケージを使用する場合、disposeメソッドで接続を正しく閉じることで、メモリリークを防止します。また、アプリのライフサイクルイベント(例:AppLifecycleState.paused)を処理して、バックグラウンドへの移行に対応します。Web(ブラウザ): 一部のブラウザでは WebSocket 接続数が制限されています。複数のタスクで 1 つの接続を再利用してください。
beforeunloadイベントを使用して、ページ終了前に接続を閉じ、古くなった接続を回避します。モバイル(iOS/Android ネイティブ): OS はアプリがバックグラウンドに移行した際にネットワーク接続を一時停止または終了することがあります。WebSocket をアクティブに保つためにバックグラウンドタスクまたはフォアグラウンドサービスを使用するか、フォアグラウンド復帰時にタスクを再初期化してください。
URL
WebSocket URL は固定です。
国際
「国際デプロイモード」では、アクセスポイントおよびデータストレージの両方が シンガポールリージョン に配置されます。モデル推論のコンピューティングリソースは中国本土を除き、グローバルに動的にスケジュールされます。
WebSocket URL:wss://dashscope-intl.aliyuncs.com/api-ws/v1/inference
中国本土
「中国本土デプロイモード」では、アクセスポイントおよびデータストレージの両方が 北京リージョン に配置されます。モデル推論のコンピューティングリソースは中国本土内に限定されます。
WebSocket URL:wss://dashscope.aliyuncs.com/api-ws/v1/inference
一般的な URL 構成エラー:
エラー: http:// や https:// で始まる URL を使用 → 正しい方法: wss:// プロトコルを必ず使用してください。
エラー: Authorization パラメーターを URL クエリ文字列に含める(例:
?Authorization=bearer <your_api_key>) → 正しい方法: Authorization パラメーターは HTTP ハンドシェイクのヘッダーに設定してください。「ヘッダー」をご参照ください。エラー: URL の末尾にモデル名やその他のパスパラメーターを追加 → 正しい方法: URL は固定です。run-task 命令 内の
payload.modelパラメーターでモデルを指定してください。
ヘッダー
リクエストに以下のヘッダーを追加してください。
パラメーター | 型 | 必須 | 説明 |
Authorization | string | はい | 認証トークンは |
user-agent | string | いいえ | サーバーがソースをトレースできるようにするクライアント識別子。 |
X-DashScope-WorkSpace | string | いいえ | Alibaba Cloud Model Studio の ワークスペース ID。 |
X-DashScope-DataInspection | string | いいえ | データコンプライアンス検査を有効にするかどうか。デフォルトは未設定または |
認証タイミングおよび一般的なエラー
認証検証は WebSocket ハンドシェイク時に実行され、run-task 命令 を送信したときではありません。Authorization ヘッダーが存在しない、または API キーが無効な場合、サーバーはハンドシェイクを拒否し、HTTP 401 または 403 エラーを返します。クライアントライブラリでは、通常 WebSocketBadStatus 例外として報告されます。
認証失敗のトラブルシューティング
WebSocket 接続が失敗した場合、以下の手順に従ってください。
API キーのフォーマットを確認: Authorization ヘッダーが
bearer <your_api_key>のフォーマットであり、`bearer` と API キーの間に半角スペースがあることを確認してください。API キーの有効性を確認: Model Studio コンソールで、API キーが削除または無効化されておらず、CosyVoice モデルを呼び出す権限を持っていることを確認してください。
ヘッダーの設定を確認: Authorization ヘッダーが WebSocket ハンドシェイク時に正しく設定されていることを確認してください。ヘッダーの設定方法はプログラミング言語によって異なります。
Python(websockets ライブラリ):
extra_headers={"Authorization": f"bearer {api_key}"}JavaScript:標準の WebSocket API はカスタムヘッダーをサポートしていません。サーバーサイドプロキシまたは ws などの別のライブラリを使用してください。
Go(gorilla/websocket):
header.Add("Authorization", fmt.Sprintf("bearer %s", apiKey))
ネットワーク接続をテスト: curl や Postman を使用して、API キーが他の HTTP サポートの DashScope API で正常に動作することをテストできます。
ブラウザ環境における WebSocket の使用
Vue3 や React などのブラウザ環境では、ネイティブのブラウザ WebSocket API にはセキュリティ制限があり、カスタムヘッダーの使用が禁止されています。ネイティブの new WebSocket(url) API は ハンドシェイク時のカスタムリクエストヘッダー(例:Authorization)を許可しません。これはブラウザのセキュリティ制限です。そのため、フロントエンドコードで API キーを直接使用して認証することはできません。
解決策: バックエンドプロキシを使用
バックエンドサービス(Node.js、Java、Python など)から CosyVoice サービスへの WebSocket 接続を確立します。バックエンドサービスでは、Authorization ヘッダーを正しく設定できます。
フロントエンドをバックエンドサービスに WebSocket で接続します。バックエンドサービスは、CosyVoice へのメッセージ転送を行うプロキシとして機能します。
メリット:このアプローチにより、API キーをバックエンドで安全に保持できます。また、認証、ログ記録、レート制限などのビジネスロジックをバックエンドで追加できます。
API キーをフロントエンドコードにハードコードしたり、ブラウザから直接送信したりしないでください。API キーを公開すると、アカウントの乗っ取り、高額な課金、データ侵害などのリスクがあります。
サンプルコード:
その他のプログラミング言語については、これらの例のロジックを流用するか、AI ツールを用いて変換してください。
フロントエンド(ネイティブ Web)+バックエンド(Node.js Express):cosyvoiceNodeJs_en.zip
フロントエンド(ネイティブ Web)+バックエンド(Python Flask):cosyvoiceFlask_en.zip
命令(クライアント → サーバー)
命令は、クライアントからサーバーへ送信される JSON 形式のメッセージであり、Text Frame 形式を用いてタスクの開始、停止、境界を制御します。
命令は厳密な順序で送信してください。そうでないとタスクが失敗する可能性があります。
run-task 命令 を送信
音声合成タスクを開始します。
返される
task_idは、その後の continue-task および finish-task 命令 で一貫して使用する必要があります。
continue-task 命令 を送信
合成対象テキストを送信します。
task-started イベント をサーバーから受信した後のみ送信してください。
finish-task 命令 を送信
音声合成タスクを終了します。
すべての continue-task 命令 を送信した後に送信してください。
1. run-task 命令:タスクの開始
この命令は音声合成タスクを開始します。音声、サンプルレート、その他のリクエストパラメーターをここで設定できます。
送信タイミング: WebSocket 接続が確立された後。
合成対象テキストを送信しないでください: run-task 命令にテキストを含めて送信すると、トラブルシューティングが困難になります。これを避けてください。代わりに、continue-task 命令 を用いてテキストを送信してください。
input フィールドは必須です: payload には input フィールド(
{})が含まれている必要があります。省略すると「task can not be null」というエラーメッセージが返されます。
例:
{
"header": {
"action": "run-task",
"task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx", // ランダムな UUID
"streaming": "duplex"
},
"payload": {
"task_group": "audio",
"task": "tts",
"function": "SpeechSynthesizer",
"model": "cosyvoice-v3-flash",
"parameters": {
"text_type": "PlainText",
"voice": "longanyang", // 音声
"format": "mp3", // 音声フォーマット
"sample_rate": 22050, // サンプルレート
"volume": 50, // ボリューム
"rate": 1, // 話速
"pitch": 1 // ピッチ
},
"input": {// input は必須であり、省略できません(空のオブジェクト {} を使用)
}
}
}header パラメーター参照:
パラメーター | 型 | 必須 | 説明 |
header.action | string | はい | 命令タイプ。 この命令では、常に "run-task" です。 |
header.task_id | string | はい | このリクエストのタスク ID。 32 文字の普遍的ユニーク識別子(UUID)。32 文字のランダムな英数字で構成され、ハイフンを含む場合(例: 後の continue-task および finish-task 命令 では、run-task 命令で使用したのと同じ task_id を使用してください。 |
header.streaming | string | はい | 固定文字列:"duplex" |
payload パラメーター参照:
パラメーター | 型 | 必須 | 説明 |
payload.task_group | string | はい | 固定文字列:"audio"。 |
payload.task | string | はい | 固定文字列:"tts"。 |
payload.function | string | はい | 固定文字列:"SpeechSynthesizer"。 |
payload.model | string | はい | 音声合成の モデル。 異なるモデルバージョンでは、対応する音声バージョンが必要です。
|
payload.input | object | はい | run-task 命令では、input フィールドは必須(省略不可)ですが、ここでは合成対象テキストを送信しないでください(空のオブジェクト
重要 よくあるエラー: input フィールドを省略したり、mode や content などの予期しないフィールドを追加したりすると、サーバーが「InvalidParameter: task can not be null」エラーでリクエストを拒否したり、接続を閉じたり(WebSocket コード 1007)します。 |
payload.parameters | |||
text_type | string | はい | 固定文字列:"PlainText"。 |
voice | string | はい | 音声合成に使用する音声。 システム音声およびクローン音声がサポートされます。
|
format | string | いいえ | 音声コーディングフォーマット。 pcm、wav、mp3(デフォルト)、opus をサポートします。 format が opus の場合、 |
sample_rate | integer | いいえ | 音声のサンプリングレート(Hz 単位)。 デフォルト:22050。 有効な値:8000、16000、22050、24000、44100、48000。 説明 デフォルトのサンプルレートは、選択した音声にとって最適なレートを表します。出力はこのレートでデフォルトで行われますが、ダウンサンプリングおよびアップサンプリングもサポートされます。 |
volume | integer | いいえ | ボリューム。 デフォルト値:50。 有効な値:[0, 100]。値 50 はデフォルトのボリュームを表します。ボリュームはこの値に比例して変化します。値 0 は無音、値 100 は最大ボリュームです。 |
rate | float | いいえ | 話速。 デフォルト値:1.0。 有効な値:[0.5, 2.0]。値 1.0 は標準の話速です。1.0 より小さい値は話速を遅くし、1.0 より大きい値は話速を速くします。 |
pitch | float | いいえ | ピッチ。この値はピッチ調整の乗数として機能しますが、知覚されるピッチ変化との関係は厳密に線形でも対数的でもありません。適切な値を見つけるにはテストを推奨します。 デフォルト値:1.0。 有効な値:[0.5, 2.0]。値 1.0 は音声の自然なピッチです。1.0 より大きい値はピッチを上げ、1.0 より小さい値はピッチを下げます。 |
enable_ssml | boolean | いいえ | SSML を有効化します。
|
bit_rate | int | いいえ | 音声ビットレート(kbps)。音声フォーマットが Opus の場合、 デフォルト値:32。 有効な値:[6, 510]。 |
word_timestamp_enabled | ブール値 | いいえ | 単語レベルのタイムスタンプを有効にするかどうかを指定します。 デフォルト値:false
この機能は、cosyvoice-v3-flash、cosyvoice-v3-plus、および cosyvoice-v2 モデルのクローン音声、および音声リストでサポート対応とマークされたシステム音声でのみ利用可能です。 word_timestamp_enabled を有効にすると、タイムスタンプ情報が result-generated イベント に表示されます。例: |
seed | int | いいえ | 生成時に使用される乱数シード。異なるシードは異なる合成結果を生成します。モデル、テキスト、音声、その他のパラメーターが同一の場合、同一のシードを使用すると同一の出力を再現できます。 デフォルト値:0。 有効な値:[0, 65535]。 |
language_hints | array[string] | いいえ | 音声合成の対象言語を指定し、合成効果を向上させます。 数字、略語、記号の発音や、あまり使われない言語の合成品質が期待通りでない場合に、このパラメーターを使用します。例:
有効な値:
注: このパラメーターは配列ですが、現在のバージョンでは最初の要素のみが処理されます。そのため、1 つの値のみを渡すことを推奨します。 重要 このパラメーターは音声合成の対象言語を指定します。これは音声クローンに使用するサンプル音声の言語とは独立しています。クローンタスクのソース言語を設定するには、「CosyVoice 音声クローン/デザイン API」をご参照ください。 |
instruction | string | いいえ | 方言、感情、話し方のスタイルなどの合成効果を制御する命令を設定します。この機能は、cosyvoice-v3.5-flash、cosyvoice-v3.5-plus、cosyvoice-v3-flash モデルのクローン音声および、音声一覧 で Instruct をサポートとマークされたシステム音声でのみ利用可能です。 長さ制限: 100 文字。 中国語文字(簡体字・繁体字、日本語漢字、韓国語漢字を含む)は 2 文字としてカウントされます。句読点、英数字、日本語/韓国語の仮名/ハングルなどその他のすべての文字は 1 文字としてカウントされます。 使用要件(モデルによって異なります):
|
enable_aigc_tag | boolean | いいえ | 生成された音声に不可視の AIGC 識別子を追加するかどうかを指定します。true に設定すると、サポートされるフォーマット(WAV、MP3、Opus)の音声に不可視の識別子が埋め込まれます。 デフォルト値:false。 この機能は cosyvoice-v3-flash、cosyvoice-v3-plus、cosyvoice-v2 のみでサポートされます。 |
aigc_propagator | string | いいえ | 不可視の AIGC 識別子の デフォルト値:Alibaba Cloud UID。 この機能は cosyvoice-v3-flash、cosyvoice-v3-plus、cosyvoice-v2 のみでサポートされます。 |
aigc_propagate_id | string | いいえ | 不可視の AIGC 識別子の デフォルト値:現在の音声合成リクエストのリクエスト ID。 この機能は cosyvoice-v3-flash、cosyvoice-v3-plus、cosyvoice-v2 のみでサポートされます。 |
hot_fix | object | いいえ | テキストホットパッチの構成。特定の単語の発音をカスタマイズしたり、合成前にテキストを置き換えたりできます。この機能は cosyvoice-v3-flash のクローン音声でのみ利用可能です。 パラメーター:
例: |
enable_markdown_filter | boolean | いいえ | Markdown フィルターを有効にするかどうかを指定します。有効にすると、システムが合成前に入力テキストから Markdown 記号を自動的に削除し、読み上げられることを防ぎます。この機能は cosyvoice-v3-flash のクローン音声でのみ利用可能です。 デフォルト値:false。 有効な値:
|
2. continue-task 命令
この命令は合成対象テキストを送信します。
すべてのテキストを 1 つの continue-task 命令で送信することも、順番に複数の continue-task 命令に分割して送信することもできます。
送信タイミング: task-started イベント を受信した後。
テキストフラグメントの送信間隔を 23 秒以上にしないでください。そうしないと、「request timeout after 23 seconds(23 秒後にリクエストタイムアウト)」というエラーが発生します。
これ以上テキストがない場合は、タスクを終了するために finish-task 命令 を送信してください。
サーバーは 23 秒のタイムアウトを強制します。クライアント側でこの設定を変更することはできません。
例:
{
"header": {
"action": "continue-task",
"task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx", // ランダムな UUID
"streaming": "duplex"
},
"payload": {
"input": {
"text": "Before my bed, moonlight shines bright, I suspect it's frost upon the ground."
}
}
}header パラメーター参照:
パラメーター | 型 | 必須 | 説明 |
header.action | string | はい | 命令タイプ。 この命令では、常に "continue-task" です。 |
header.task_id | string | はい | このリクエストのタスク ID。 run-task 命令 で使用した task_id と一致している必要があります。 |
header.streaming | string | はい | 固定文字列:"duplex" |
payload パラメーター参照:
パラメーター | 型 | 必須 | 説明 |
input.text | string | はい | 合成対象テキスト。 |
3. finish-task 命令:タスクの終了
この命令は音声合成タスクを終了します。
必ずこの命令を送信してください。 そうしないと、以下の問題が発生する可能性があります。
不完全な音声: サーバーは不完全なキャッシュされた文を強制的に合成しないため、終端部分が欠落します。
接続タイムアウト: 最後の continue-task 命令 を送信してから finish-task を送信するまでの間隔が 23 秒を超えると、接続がタイムアウトして閉じられます。
課金の問題: 正常に終了しないタスクは、不正確な使用量情報を返す可能性があります。
送信タイミング: すべての continue-task 命令 を送信した直後に 即座に 送信してください。音声の再生完了を待ったり、送信を遅らせたりしないでください。そうするとタイムアウトが発生する可能性があります。
例:
{
"header": {
"action": "finish-task",
"task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
"streaming": "duplex"
},
"payload": {
"input": {}//input は必須であり、省略するとエラーが返されます
}
}header パラメーター参照:
パラメーター | 型 | 必須 | 説明 |
header.action | string | はい | 命令タイプ。 この命令では、常に "finish-task" です。 |
header.task_id | string | はい | このリクエストのタスク ID。 run-task 命令 で使用した task_id と一致している必要があります。 |
header.streaming | string | はい | 固定文字列:"duplex" |
payload パラメーター参照:
パラメーター | 型 | 必須 | 説明 |
payload.input | object | はい | 固定フォーマット:{}。 |
イベント(サーバー → クライアント)
イベントは、サーバーからクライアントへ送信される JSON 形式のメッセージであり、それぞれが処理の段階を表します。
サーバーはバイナリ音声を別途送信します。これはいかなるイベントにも含まれません。
1. task-started イベント:タスク開始
task-started イベントを受信すると、タスクが正常に開始されたことを意味します。このイベントを受信した後のみ、continue-task または finish-task 命令 を送信してください。そうしないと、タスクが失敗します。
task-started イベントの payload にはコンテンツが含まれません。
例:
{
"header": {
"task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
"event": "task-started",
"attributes": {}
},
"payload": {}
}header パラメーター参照:
パラメーター | 型 | 説明 |
header.event | string | イベントタイプ。 このイベントでは、常に "task-started" です。 |
header.task_id | string | クライアントが生成したタスク ID。 |
2. result-generated イベント
continue-task および finish-task 命令 を送信している間、サーバーは継続的に result-generated イベントを返します。
音声データを対応するテキストにリンクするため、サーバーは result-generated イベントとともに音声に文のメタデータを含めます。サーバーは自動的に入力テキストを文に分割します。各文の合成は、以下の 3 つのサブイベントで構成されます。
sentence-begin:文の開始を示し、合成対象テキストを返します。sentence-synthesis:音声データチャンクを示します。各イベントの直後に、WebSocket のバイナリチャンネルを介して音声データフレームが送信されます。1 つの文は複数の
sentence-synthesisイベント(チャンクごとに 1 つ)を生成します。クライアントは、これらの音声チャンクを順序通りに受信し、同一のファイルに追加する必要があります。
各
sentence-synthesisイベントは、それに続く音声フレームと 1 対 1 で対応しており、ミスアライメントは発生しません。
sentence-end:文の終了を示し、文のテキストおよび累積課金文字数を返します。
payload.output.type フィールドを使用して、サブイベントのタイプを判別します。
例:
sentence-begin
{
"header": {
"task_id": "3f2d5c86-0550-45c0-801f-xxxxxxxxxx",
"event": "result-generated",
"attributes": {}
},
"payload": {
"output": {
"sentence": {
"index": 0,
"words": []
},
"type": "sentence-begin",
"original_text": "Before my bed, moonlight shines bright,"
}
}
}sentence-synthesis
{
"header": {
"task_id": "3f2d5c86-0550-45c0-801f-xxxxxxxxxx",
"event": "result-generated",
"attributes": {}
},
"payload": {
"output": {
"sentence": {
"index": 0,
"words": []
},
"type": "sentence-synthesis"
}
}
}sentence-end
{
"header": {
"task_id": "3f2d5c86-0550-45c0-801f-xxxxxxxxxx",
"event": "result-generated",
"attributes": {}
},
"payload": {
"output": {
"sentence": {
"index": 0,
"words": []
},
"type": "sentence-end",
"original_text": "Before my bed, moonlight shines bright,"
},
"usage": {
"characters": 11
}
}
}header パラメーター参照:
パラメーター | 型 | 説明 |
header.event | string | イベントタイプ。 このイベントでは、常に "result-generated" です。 |
header.task_id | string | クライアントが生成したタスク ID。 |
header.attributes | object | 追加属性(通常は空のオブジェクト)。 |
payload パラメーター参照:
パラメーター | 型 | 説明 |
payload.output.type | string | サブイベントタイプ。 値:
イベントフロー全体 合成する各文に対して、サーバーは次の順序でイベントを返します。
|
payload.output.sentence.index | 整数 | 文番号。0 から始まります。 |
payload.output.sentence.words | 配列 | 文字に関する情報の配列です。 |
payload.output.sentence.words.text | 文字列 | 単語のテキスト。 |
payload.output.sentence.words.begin_index | integer | 単語が文内で開始する位置のインデックス(0 から数える)。 |
payload.output.sentence.words.end_index | 整数 | 文中の単語の終了位置インデックス。1から数えます。 |
payload.output.sentence.words.begin_time | 整数 | 単語の音声の開始タイムスタンプ (ミリ秒)。 |
payload.output.sentence.words.end_time | integer | 単語の音声の終了タイムスタンプ、単位はミリ秒です。 |
payload.output.original_text | 文字列 | ユーザーの入力テキストを分割した後の文の内容です。最後の文では、このフィールドが省略されることがあります。 |
payload.usage.characters | integer | このリクエストでこれまでに課金対象となった文字数の合計です。1 つのタスク内では、 |
3. task-finished イベント:タスクの終了
task-finished イベントを受信すると、タスクは終了したことになります。
タスクが終了した後、WebSocket 接続を閉じて終了するか、接続を再利用して新しい run-task 命令を送信できます (「接続のオーバーヘッドと再利用」をご参照ください)。
例:
{
"header": {
"task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
"event": "task-finished",
"attributes": {
"request_uuid": "0a9dba9e-d3a6-45a4-be6d-xxxxxxxxxxxx"
}
},
"payload": {
"output": {
"sentence": {
"words": []
}
},
"usage": {
"characters": 13
}
}
}header パラメーターリファレンス:
パラメーター | タイプ | 説明 |
header.event | 文字列 | イベントタイプ。 このイベントでは、常に "task-finished" です。 |
header.task_id | 文字列 | クライアントによって生成されたタスク ID。 |
header.attributes.request_uuid | 文字列 | リクエスト ID。問題の診断のために、この ID を CosyVoice の開発者に提供してください。 |
payload パラメーターリファレンス:
パラメーター | タイプ | 説明 |
payload.usage.characters | integer | このリクエストで現在までに課金された合計文字数。
1 つのタスク内で、 |
4. task-failed イベント:タスクの失敗
task-failed イベントを受信した場合、タスクが失敗したことを示します。WebSocket 接続を閉じ、エラーを処理してください。エラーメッセージを分析し、コードの問題が原因で失敗した場合は、コードを修正してください。
例:
{
"header": {
"task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
"event": "task-failed",
"error_code": "InvalidParameter",
"error_message": "[tts:]Engine return error code: 418",
"attributes": {}
},
"payload": {}
}header パラメーターリファレンス:
パラメーター | 型 | 説明 |
header.event | 文字列 | イベントタイプ。 このイベントでは、常にタスクが失敗します。 |
header.task_id | 文字列 | クライアントによって生成されたタスク ID。 |
header.error_code | 文字列 | エラータイプの詳細。 |
header.error_message | 文字列 | エラーの詳細な理由。 |
タスク中断方法
ストリーミング合成中に、現在のタスクを早期に中断できます。たとえば、ユーザーが再生をキャンセルしたり、ライブ会話を中断したりする場合など、以下のいずれかの方法を使用します。
中断モード | サーバーの動作 | ユースケース |
接続を直接閉じる |
| 即時中断: ユーザーが再生をキャンセルする、コンテンツを切り替える、またはアプリを終了する。 |
finish-task コマンドを送信 |
| 正常終了: 新しいテキストの送信を停止しても、キャッシュされたコンテンツの音声は受信し続けます。 |
接続オーバーヘッドおよび再利用
WebSocket サービスは、リソース効率を向上させ、接続設定のオーバーヘッドを回避するために、接続の再利用をサポートしています。
サーバーがクライアントから run-task 命令 を受信すると、新しいタスクを開始します。クライアントが finish-task 命令 を送信すると、タスク完了時にサーバーは task-finished イベント を返します。タスク終了後、WebSocket 接続は別の run-task 命令 を送信して次のタスクを開始することで再利用できます。
新しい run-task 命令 は、サーバーが task-finished イベント を返した後のみ送信できます。
再利用された接続上の異なるタスクは、異なる task_id を使用する必要があります。
タスク実行中にタスクが失敗した場合、サーバーは task-failed イベント を返し、接続を閉じます。この接続は再利用できません。
タスク終了後 60 秒以内に新しいタスクが開始されない場合、接続はタイムアウトして自動的に閉じられます。
パフォーマンスメトリクスおよび同時実行数制限
同時実行数制限
詳細については、「レート制限」をご参照ください。
同時実行数のクォータを増やすには、たとえばより多くの同時接続をサポートするために、カスタマーサポートに連絡してください。クォータ調整には審査が必要であり、通常 1 ~ 3 営業日以内に完了します。
ベストプラクティス: リソース使用効率を向上させるため、各タスクごとに新しい接続を開くのではなく、単一の WebSocket 接続を複数のタスクで再利用できます。「接続オーバーヘッドおよび再利用」をご参照ください。
接続パフォーマンスおよびレイテンシー
一般的な接続時間:
中国本土のクライアント:WebSocket 接続の確立(newWebSocket から onOpen まで)は通常 200 ~ 1000 ミリ秒かかります。
クロスボーダー接続(香港または国際リージョンなど):接続レイテンシーは 1 ~ 3 秒に達する場合があります。まれに 10 ~ 30 秒に達することもあります。
接続時間の長期化のトラブルシューティング:
WebSocket 接続の確立に 30 秒以上かかる場合、原因は以下のいずれかの問題である可能性があります。
ネットワークの問題: クライアントとサーバー間のネットワーク遅延が高い場合(クロスボーダー接続や ISP の品質の悪さによる遅延など)。
DNS 解決の遅延: dashscope.aliyuncs.com の DNS 解決に時間がかかる場合。8.8.8.8 などのパブリック DNS を使用するか、ローカルホストファイルを構成してみてください。
TLS ハンドシェイクの遅延: クライアントの TLS バージョンが古い場合、または証明書検証が遅い場合。TLS 1.2 以降のバージョンを使用することを推奨します。
プロキシまたはファイアウォール: 企業ネットワークが WebSocket 接続をブロックしたり、プロキシの使用を要求したりする場合があります。
トラブルシューティングツール:
Wireshark または tcpdump を使用して、TCP ハンドシェイク、TLS ハンドシェイク、および WebSocket アップグレードフェーズのタイミングを分析できます。
curl を使用して HTTP 接続のレイテンシーをテストできます。
curl -w "@curl-format.txt" -o /dev/null -s https://dashscope.aliyuncs.com
CosyVoice WebSocket API は中国本土の北京リージョンにデプロイされています。クライアントが香港や海外リージョンなど別のリージョンにある場合、近くのリレーサーバーまたは CDN を使用して接続を高速化できます。
音声生成パフォーマンス
合成速度:
リアルタイムファクター(RTF):CosyVoice モデルは通常、リアルタイム速度の 0.1 ~ 0.5 倍で音声を合成します。これは、1 秒の音声を生成するのに 0.1 ~ 0.5 秒かかることを意味します。実際の速度は、モデルバージョン、テキスト長、およびサーバー負荷によって異なります。
最初のパケットのレイテンシー:continue-task 命令を送信してから最初の音声チャンクを受信するまでのレイテンシーは、通常 200 ~ 800 ミリ秒です。
サンプルコード
このサンプルコードは基本的なサービス接続のみを示しています。特定のユースケースに合わせて、本番環境に対応したロジックを実装する必要があります。
WebSocket クライアントコードを作成する際は、非同期プログラミングを使用してメッセージを同時に送受信してください。以下の手順に従います。
エラーコード
エラーが発生した場合、トラブルシューティングについては「エラーメッセージ」をご参照ください。
よくある質問
機能、課金、およびレート制限
Q:発音が不正確な場合、どうすればよいですか?
SSML を使用して、音声合成効果をカスタマイズできます。
Q:なぜ HTTP/HTTPS ではなく WebSocket を使用するのですか? RESTful API を提供しないのはなぜですか?
音声サービスが HTTP/HTTPS や RESTful API ではなく WebSocket を使用するのは、全二重通信が必要だからです。WebSocket は、サーバーとクライアントの両方が合成や認識のリアルタイム進捗更新などのデータを能動的にプッシュすることを可能にします。HTTP 上の RESTful API はクライアントが開始するリクエスト・レスポンスサイクルのみをサポートしており、リアルタイムのインタラクション要件を満たすことができません。
Q:音声合成は文字数単位で課金されます。各合成の文字数を確認または取得するにはどうすればよいですか?
サーバーの result-generated イベント 内の payload.usage.characters パラメーターから文字数を取得できます。最後に受信した result-generated イベントの値を使用してください。
トラブルシューティング
Q:リクエスト ID を取得するにはどうすればよいですか?
以下の 2 つの方法で取得できます。
サーバー側から result-generated イベント で返される情報をパースできます。
サーバーから task-finished イベント で返される情報をパースできます。
Q:SSML が失敗するのはなぜですか?
この問題を段階的にトラブルシューティングしてください。
制限事項 に正しく従っていることを確認してください。
SSML を正しく呼び出していることを確認してください。詳細については、「SSML マークアップ言語のサポート」をご参照ください。
テキストがプレーンテキストであり、フォーマット要件を満たしていることを確認してください。詳細については、「SSML マークアップ言語の概要」をご参照ください。
Q:音声が再生できないのはなぜですか?
以下のシナリオを一つずつ確認してください。
音声が完全なファイル(例:xx.mp3)として保存されている場合。
音声フォーマットの一貫性:リクエストパラメーターで設定された音声フォーマットがファイル拡張子と一致していることを確認してください。たとえば、WAV フォーマットを設定しているのに .mp3 拡張子で保存すると、再生が失敗します。
プレーヤーの互換性:ご利用のプレーヤーが音声ファイルのフォーマットとサンプルレートをサポートしていることを確認してください。一部のプレーヤーは、高いサンプルレートや特定の音声エンコーディングをサポートしていない場合があります。
音声がストリームで再生される場合。
音声ストリームを完全なファイルとして保存し、プレーヤーで再生を試みてください。ファイルが再生できない場合は、シナリオ 1 のトラブルシューティング方法を参照してください。
ファイルが正常に再生される場合、問題はストリーミング再生の実装にある可能性があります。ご利用のプレーヤーがストリーミング再生をサポートしていることを確認してください。
ストリーミング再生をサポートする一般的なツールとライブラリには、FFmpeg、PyAudio(Python)、AudioFormat(Java)、MediaSource(JavaScript)などがあります。
Q:音声再生が途切れるのはなぜですか?
以下のシナリオを一つずつ確認してください。
テキスト送信速度の確認:テキストセグメント間の間隔が適切であることを確認してください。前の音声セグメントの再生が終了した後、次のセグメントがすぐに送信されない状況を避けてください。
コールバック関数のパフォーマンスの確認:
コールバック関数に過剰なビジネスロジックがあり、ブロッキングを引き起こしていないか確認してください。
コールバック関数は WebSocket スレッドで実行されます。ブロッキングが発生すると、WebSocket がネットワークパケットを時間内に受信できず、音声再生が途切れる可能性があります。
WebSocket スレッドのブロッキングを避けるため、音声データを別のバッファーに書き込み、別のスレッドで処理することを推奨します。
ネットワーク安定性の確認:ネットワーク接続が安定していることを確認し、ネットワークの変動による音声送信の中断や遅延を避けてください。
Q:音声合成に時間がかかるのはなぜですか?
以下の手順でトラブルシューティングしてください。
入力間隔の確認
入力間隔を確認してください。ストリーミング音声合成を使用している場合、テキストセグメント間の送信間隔が長すぎないか(たとえば、数秒の遅延)確認してください。間隔が長いと、合計合成時間が増加します。
パフォーマンスメトリクスの分析。
最初のパケットのレイテンシー:通常約 500 ミリ秒。
RTF(RTF = 合計合成時間 / 音声持続時間):通常 1.0 未満。
Q:合成された音声の発音の誤りを処理するにはどうすればよいですか?
SSML の <phoneme> タグ を使用して、正しい発音を指定してください。
Q:音声が返されないのはなぜですか? 音声の末尾のテキストの一部が欠落しているのはなぜですか?(音声の欠落)
finish-task 命令 の送信を忘れていないか確認してください。合成中、サーバーは十分なテキストをキャッシュするまで待機してから開始します。finish-task の送信を忘れると、キャッシュ内の末尾のテキストが音声に変換されない可能性があります。
Q:音声ストリームの順序が乱れ、再生が文字化けするのはなぜですか?
この問題を以下の 2 つの領域でトラブルシューティングしてください。
1 つの合成タスクに対する run-task、continue-task、および finish-task 命令 がすべて同じ
task_idを使用していることを確認してください。非同期操作によって、バイナリデータが受信された順序と異なる順序で音声ファイルが書き込まれていないか確認してください。
Q:WebSocket 接続エラーを処理するにはどうすればよいですか?
WebSocket 接続のクローズ(コード 1007)を処理するにはどうすればよいですか?
run-task 命令を送信した後、WebSocket 接続がコード 1007 で即座に閉じられます。
根本原因: サーバーがプロトコルまたはデータフォーマットのエラーを検出し、切断します。一般的な理由は以下のとおりです。
run-task の payload に無効なフィールドがある場合(例:
"input": {}以外のフィールドを追加した場合)。JSON フォーマットエラー(例:カンマの欠落や括弧の不一致)。
必須フィールド(task_id や action など)の欠落。
解決策:
JSON フォーマットの検証:リクエストボディの構文を確認してください。
必須フィールドの確認:header.action、header.task_id、header.streaming、payload.task_group、payload.task、payload.function、payload.model、および payload.input がすべて設定されていることを確認してください。
無効なフィールドの削除:run-task の payload.input では、空のオブジェクト
{}またはテキストフィールドのみを許可してください。他のフィールドを追加しないでください。
WebSocketBadStatus、401 Unauthorized、または 403 Forbidden エラーを処理するにはどうすればよいですか?
WebSocket 接続が WebSocketBadStatus、401 Unauthorized、または 403 Forbidden で失敗します。
根本原因: 認証失敗。サーバーは WebSocket ハンドシェイク中に Authorization ヘッダーを検証します。無効な API キーまたは欠落している API キーは拒否されます。
解決策: 詳細については、「認証失敗のトラブルシューティング」をご参照ください。
権限および認証
Q:API キーを CosyVoice 音声合成サービスのみに制限するにはどうすればよいですか?(権限の隔離)
ワークスペースを作成し、特定のモデルにのみ権限を付与することで、API キーのスコープを制限できます。詳細については、「ワークスペースの管理」をご参照ください。
その他の質問
GitHub の QA をご参照ください。