テキストから音声を合成する方法

リファレンス ドキュメント | パッケージ (NuGet) | GitHub のその他のサンプル

この攻略ガイドでは、テキスト読み上げ合成を行うための一般的な設計パターンについて説明します。

次の領域の詳細については、「テキスト読み上げとは」を参照してください。

  • インメモリ ストリームとしての応答の取得。
  • 出力のサンプル レートとビット レートのカスタマイズ。
  • 音声合成マークアップ言語 (SSML) を使用した合成要求の送信。
  • ニューラル音声の使用。
  • イベントのサブスクライブと、結果に対するアクションの実行。

合成言語と音声を選択する

Speech サービスのテキスト読み上げ機能では、400 を超える音声と、140 を超える言語およびバリアントがサポートされています。 すべてのリストを入手することも、音声ギャラリーでそれらを試すこともできます。

入力テキストに合わせて SpeechConfig の言語または音声を指定し、必要な音声を使用します。 次のコード スニペットは、この手法のしくみを示しています:

static async Task SynthesizeAudioAsync()
{
    var speechConfig = SpeechConfig.FromSubscription("YourSpeechKey", "YourSpeechRegion");
    // Set either the `SpeechSynthesisVoiceName` or `SpeechSynthesisLanguage`.
    speechConfig.SpeechSynthesisLanguage = "en-US"; 
    speechConfig.SpeechSynthesisVoiceName = "en-US-AvaMultilingualNeural";
}

すべてのニューラル音声は多言語で、独自の言語と英語で流暢に読み上げます。 たとえば、英語の入力テキストが "I'm excited to try text to speech" で、es-ES-ElviraNeural を選択した場合、テキストはスペイン語アクセントの英語で読み上げられます。

入力テキストの言語が音声で読み上げられない場合、Speech サービスでは合成された音声が作成されません。 サポートされているニューラル音声の完全な一覧については、「Azure Cognitive Service for Speech サービスの言語と音声のサポート」を参照してください。

Note

既定の音声は、Voice List API からロケールに従って返される最初の音声です。

読み上げられる音声は、次の優先順序で決定されます。

  • SpeechSynthesisVoiceName または SpeechSynthesisLanguage を設定していない場合、en-US の既定の音声が読み上げられます。
  • SpeechSynthesisLanguage のみを設定した場合、指定したロケールの既定の音声が読み上げられます。
  • SpeechSynthesisVoiceNameSpeechSynthesisLanguage の両方を設定した場合、SpeechSynthesisLanguage の設定は無視されます。 SpeechSynthesisVoiceName を使用して指定した音声が読み上げられます。
  • 音声要素を音声合成マークアップ言語 (SSML) で設定した場合、SpeechSynthesisVoiceNameSpeechSynthesisLanguage の設定は無視されます。

音声をファイルに合成する

SpeechSynthesizer オブジェクトを作成します。 このオブジェクトにより、テキスト読み上げ変換と、スピーカー、ファイル、またはその他の出力ストリームへの出力が実行されます。 SpeechSynthesizer は、パラメーターとして以下を受け取ります。

  • 前の手順で作成した SpeechConfig オブジェクト。
  • 出力結果をどのように処理する必要があるかを指定している AudioConf オブジェクト。
  1. FromWavFileOutput() 関数を使用して wav ファイルに出力を自動的に書き込む AudioConfig インスタンスを作成します。 これを using ステートメントでインスタンス化します。

    static async Task SynthesizeAudioAsync()
    {
        var speechConfig = SpeechConfig.FromSubscription("YourSpeechKey", "YourSpeechRegion");
        using var audioConfig = AudioConfig.FromWavFileOutput("path/to/write/file.wav");
    }
    

    このコンテキストの using ステートメントによって、アンマネージド リソースが自動的に破棄され、破棄後にオブジェクトがスコープ外になります。

  2. 別の using ステートメントを使用して SpeechSynthesizer インスタンスをインスタンス化します。 speechConfig オブジェクトと audioConfig オブジェクトをパラメーターとして渡します。 音声を合成してファイルに書き込むには、文字列で SpeakTextAsync() を実行します。

static async Task SynthesizeAudioAsync()
{
    var speechConfig = SpeechConfig.FromSubscription("YourSpeechKey", "YourSpeechRegion");
    using var audioConfig = AudioConfig.FromWavFileOutput("path/to/write/file.wav");
    using var speechSynthesizer = new SpeechSynthesizer(speechConfig, audioConfig);
    await speechSynthesizer.SpeakTextAsync("I'm excited to try text to speech");
}

プログラムを実行すると、合成された .wav ファイルが作成され、指定した場所に書き込まれます。 この結果は、最も基本的な使用方法の好例です。 次に、カスタム シナリオに対応できるように、出力をカスタマイズし、出力応答をインメモリ ストリームとして処理することができます。

スピーカー出力に合成する

合成された音声をスピーカーなどの現在アクティブな出力デバイスに出力するには、SpeechSynthesizer インスタンスの作成時に AudioConfig パラメーターを省略します。 次に例を示します。

static async Task SynthesizeAudioAsync()
{
    var speechConfig = SpeechConfig.FromSubscription("YourSpeechKey", "YourSpeechRegion");
    using var speechSynthesizer = new SpeechSynthesizer(speechConfig);
    await speechSynthesizer.SpeakTextAsync("I'm excited to try text to speech");
}

結果をインメモリ ストリームとして取得する

結果として得られたオーディオ データは、ファイルに直接書き込むのではなく、インメモリ ストリームとして使用できます。 インメモリ ストリームを使用すると、カスタム動作を構築できます:

  • 結果として得られるバイト配列を、カスタム ダウンストリーム サービス向けのシーク可能なストリームとして抽象化します。
  • 結果を他の API やサービスと統合します。
  • オーディオ データの変更、カスタム .wav ヘッダーの記述、関連するタスクの実行を行います。

前の例にこの変更を加えることができます。 まず、AudioConfig ブロックを削除します。これは、この時点から先は出力動作を手動で管理して制御を強化するためです。 SpeechSynthesizer コンストラクターの AudioConfignull を渡します。

Note

前出のスピーカー出力の例で行ったように省略するのではなく、AudioConfignull を渡すと、既定では、現在のアクティブな出力デバイスではオーディオが再生されなくなります。

結果を SpeechSynthesisResult 変数に保存します。 AudioData プロパティには、出力データの byte [] インスタンスが格納されます。 この byte [] インスタンスを手動で操作することも、AudioDataStream クラスを使用してインメモリ ストリームを管理することもできます。

この例では、AudioDataStream.FromResult() 静的関数を使用して、結果からストリームを取得しています。

static async Task SynthesizeAudioAsync()
{
    var speechConfig = SpeechConfig.FromSubscription("YourSpeechKey", "YourSpeechRegion");
    using var speechSynthesizer = new SpeechSynthesizer(speechConfig, null);

    var result = await speechSynthesizer.SpeakTextAsync("I'm excited to try text to speech");
    using var stream = AudioDataStream.FromResult(result);
}

この時点で、結果として得られた stream オブジェクトを使用して、任意のカスタム動作を実装できます。

オーディオ形式をカスタマイズする

以下を含むオーディオ出力属性をカスタマイズできます。

  • オーディオ ファイルの種類
  • サンプル速度
  • ビット深度

オーディオ形式を変更するには、SpeechConfig オブジェクトで SetSpeechSynthesisOutputFormat() 関数を使用します。 この関数では、SpeechSynthesisOutputFormat 型の enum インスタンスが必要です。 enum を使用して出力形式を選択します。 使用可能な形式については、「オーディオ形式の一覧」を参照してください。

要件に応じて、異なるファイルの種類に向けたさまざまなオプションがあります。 定義上、Raw24Khz16BitMonoPcm のような未加工の形式には、オーディオ ヘッダーが含まれていません。 未加工の形式は、次のいずれかの状況でのみ使用してください。

  • ダウンストリームの実装で未加工のビットストリームをデコードできることがわかっている。
  • ビット深度、サンプル レート、チャネル数のような要因に基づいて、ヘッダーを手動で構築する予定である。

この例では、SpeechConfig オブジェクトに SpeechSynthesisOutputFormat を設定して、忠実度が高い RIFF 形式 Riff24Khz16BitMonoPcm を指定しています。 前のセクションの例と同様に、AutoDataStream を使用して結果のインメモリ ストリームを取得し、それをファイルに書き込みます。

static async Task SynthesizeAudioAsync()
{
    var speechConfig = SpeechConfig.FromSubscription("YourSpeechKey", "YourSpeechRegion");
    speechConfig.SetSpeechSynthesisOutputFormat(SpeechSynthesisOutputFormat.Riff24Khz16BitMonoPcm);

    using var speechSynthesizer = new SpeechSynthesizer(speechConfig, null);
    var result = await speechSynthesizer.SpeakTextAsync("I'm excited to try text to speech");

    using var stream = AudioDataStream.FromResult(result);
    await stream.SaveToWaveFileAsync("path/to/write/file.wav");
}

プログラムを実行すると、指定したパスに .wav ファイルが書き込まれます。

SSML を使用して音声の特徴をカスタマイズする

SSML を使用すると、XML スキーマから要求を送信して、テキスト読み上げ出力でのピッチ、発音、読み上げ速度、ボリューム、その他の面を微調整できます。 このセクションでは、音声を変更する例を示します。 詳しくは、「音声合成マークアップ言語の概要」をご覧ください。

SSML を使用したカスタマイズを開始するには、音声を切り替える小規模変更を加えます。

  1. ルート プロジェクト ディレクトリに SSML 構成用の新しい XML ファイルを作成します。

    <speak version="1.0" xmlns="https://www.w3.org/2001/10/synthesis" xml:lang="en-US">
      <voice name="en-US-AvaMultilingualNeural">
        When you're on the freeway, it's a good idea to use a GPS.
      </voice>
    </speak>
    

    この例では、ファイルは ssml.xmlです。 ルート要素は常に <speak> です。 テキストを <voice> 要素にラップすると、name パラメーターを使用して音声を変更できます。 サポートされているニューラル音声の一覧については、「サポートされている言語」を参照してください。

  2. XML ファイルを参照するように音声合成要求を変更する必要があります。 要求はほとんど同じですが、SpeakTextAsync() 関数を使用する代わりに、SpeakSsmlAsync() を使用します。 この関数は XML 文字列を必要とします。 まず、File.ReadAllText() を使用して、最初に SSML 構成を文字列として読み込みます。 この時点からは、結果のオブジェクトは前の例とまったく同じです。

    Note

    Visual Studio を使用している場合、既定では、ビルド構成からは XML ファイルが見つからない可能性があります。 XML ファイルを右クリックし、[プロパティ] を選択します。 [ビルド アクション][コンテンツ] に変更します。 [出力ディレクトリにコピー][常にコピーする] に変更します。

    public static async Task SynthesizeAudioAsync()
    {
        var speechConfig = SpeechConfig.FromSubscription("YourSpeechKey", "YourSpeechRegion");
        using var speechSynthesizer = new SpeechSynthesizer(speechConfig, null);
    
        var ssml = File.ReadAllText("./ssml.xml");
        var result = await speechSynthesizer.SpeakSsmlAsync(ssml);
    
        using var stream = AudioDataStream.FromResult(result);
        await stream.SaveToWaveFileAsync("path/to/write/file.wav");
    }
    

Note

SSML を使用せずに音声を変更するには、SpeechConfig.SpeechSynthesisVoiceName = "en-US-AvaMultilingualNeural"; を使用して SpeechConfig のプロパティを設定します。

シンセサイザー イベントをサブスクライブする

テキスト読み上げ処理と結果に関する、より多くの分析情報が必要になる場合があります。 たとえば、シンセサイザーが開始および停止したときにそれを知りたい場合や、合成中に発生した他のイベントについて知りたい場合があります。

テキスト読み上げに SpeechSynthesizer を使用している間は、次の表のイベントをサブスクライブできます。

Event 説明 使用事例
BookmarkReached ブックマークに到達したことを通知します。 ブックマーク到達イベントをトリガーするには、SSMLbookmark 要素が必要です。 このイベントは、合成の開始から bookmark 要素までの間の出力オーディオの経過時間を報告します。 イベントの Text プロパティは、ブックマークの mark 属性で設定した文字列値です。 bookmark 要素は読み上げられません。 bookmark 要素を使用すると、SSML にカスタム マーカーを挿入して、オーディオ ストリーム内の各マーカーのオフセットを取得できます。 bookmark 要素は、テキストまたはタグのシーケンス内の特定の場所を参照するために使用できます。
SynthesisCanceled 音声合成が取り消されたことを通知します。 合成が取り消されたときに、それを確認できます。
SynthesisCompleted 音声合成が完了したことを通知します。 合成が完了したときに、それを確認できます。
SynthesisStarted 音声合成が開始したことを通知します。 合成が開始したときに、それを確認できます。
Synthesizing 音声合成が進行中であることを通知します。 このイベントは、SDK が Speech サービスからオーディオ チャンクを受信するたびに発生します。 合成が進行中のときに、それを確認できます。
VisemeReceived 口形素イベントが受信されたことを通知します。 観察された発話における主要な姿勢を表すために、口形素がよく使用されます。 主要な姿勢には、特定の音素を生成するときの唇、顎、舌の位置などが含まれます。 口形素を使用して、スピーチ音声の再生に伴って、キャラクターに顔のアニメーションを付けることができます。
WordBoundary 単語境界が受信されたことを通知します。 このイベントは、新しい音声、句読点、文それぞれの先頭で発生します。 イベントによって、出力音声の開始から現在の単語までのタイム オフセット (ティック単位) が報告されます。 このイベントは、読み上げられようとしている単語の直前に、入力テキスト (または SSML) の文字位置も報告します。 このイベントは、一般にテキストおよび対応するオーディオの相対位置を取得するために使用されます。 新しい単語について知り、タイミングに基づいてアクションを実行できます。 たとえば、単語を読み上げ時に強調表示するタイミングと時間を決定するのに役立つ情報を取得できます。

Note

イベントは、出力オーディオ データが使用可能になると発生します。これは、出力デバイスへの再生よりも高速です。 呼び出し元は、ストリーミングと実際の時間を適切に同期する必要があります。

音声合成のためにイベントをサブスクライブする方法を示す例を次に示します。 クイックスタートの手順に従うことができますが、その Program.cs ファイルの内容を次の C# コードに置き換えます:

using Microsoft.CognitiveServices.Speech;

class Program 
{
    // This example requires environment variables named "SPEECH_KEY" and "SPEECH_REGION"
    static string speechKey = Environment.GetEnvironmentVariable("SPEECH_KEY");
    static string speechRegion = Environment.GetEnvironmentVariable("SPEECH_REGION");

    async static Task Main(string[] args)
    {
        var speechConfig = SpeechConfig.FromSubscription(speechKey, speechRegion);
         
        var speechSynthesisVoiceName  = "en-US-AvaMultilingualNeural";  
        var ssml = @$"<speak version='1.0' xml:lang='en-US' xmlns='http://www.w3.org/2001/10/synthesis' xmlns:mstts='http://www.w3.org/2001/mstts'>
            <voice name='{speechSynthesisVoiceName}'>
                <mstts:viseme type='redlips_front'/>
                The rainbow has seven colors: <bookmark mark='colors_list_begin'/>Red, orange, yellow, green, blue, indigo, and violet.<bookmark mark='colors_list_end'/>.
            </voice>
        </speak>";

        // Required for sentence-level WordBoundary events
        speechConfig.SetProperty(PropertyId.SpeechServiceResponse_RequestSentenceBoundary, "true");

        using (var speechSynthesizer = new SpeechSynthesizer(speechConfig))
        {
            // Subscribe to events

            speechSynthesizer.BookmarkReached += (s, e) =>
            {
                Console.WriteLine($"BookmarkReached event:" +
                    $"\r\n\tAudioOffset: {(e.AudioOffset + 5000) / 10000}ms" +
                    $"\r\n\tText: \"{e.Text}\".");
            };

            speechSynthesizer.SynthesisCanceled += (s, e) =>
            {
                Console.WriteLine("SynthesisCanceled event");
            };

            speechSynthesizer.SynthesisCompleted += (s, e) =>
            {                
                Console.WriteLine($"SynthesisCompleted event:" +
                    $"\r\n\tAudioData: {e.Result.AudioData.Length} bytes" +
                    $"\r\n\tAudioDuration: {e.Result.AudioDuration}");
            };

            speechSynthesizer.SynthesisStarted += (s, e) =>
            {
                Console.WriteLine("SynthesisStarted event");
            };

            speechSynthesizer.Synthesizing += (s, e) =>
            {
                Console.WriteLine($"Synthesizing event:" +
                    $"\r\n\tAudioData: {e.Result.AudioData.Length} bytes");
            };

            speechSynthesizer.VisemeReceived += (s, e) =>
            {
                Console.WriteLine($"VisemeReceived event:" +
                    $"\r\n\tAudioOffset: {(e.AudioOffset + 5000) / 10000}ms" +
                    $"\r\n\tVisemeId: {e.VisemeId}");
            };

            speechSynthesizer.WordBoundary += (s, e) =>
            {
                Console.WriteLine($"WordBoundary event:" +
                    // Word, Punctuation, or Sentence
                    $"\r\n\tBoundaryType: {e.BoundaryType}" +
                    $"\r\n\tAudioOffset: {(e.AudioOffset + 5000) / 10000}ms" +
                    $"\r\n\tDuration: {e.Duration}" +
                    $"\r\n\tText: \"{e.Text}\"" +
                    $"\r\n\tTextOffset: {e.TextOffset}" +
                    $"\r\n\tWordLength: {e.WordLength}");
            };

            // Synthesize the SSML
            Console.WriteLine($"SSML to synthesize: \r\n{ssml}");
            var speechSynthesisResult = await speechSynthesizer.SpeakSsmlAsync(ssml);

            // Output the results
            switch (speechSynthesisResult.Reason)
            {
                case ResultReason.SynthesizingAudioCompleted:
                    Console.WriteLine("SynthesizingAudioCompleted result");
                    break;
                case ResultReason.Canceled:
                    var cancellation = SpeechSynthesisCancellationDetails.FromResult(speechSynthesisResult);
                    Console.WriteLine($"CANCELED: Reason={cancellation.Reason}");

                    if (cancellation.Reason == CancellationReason.Error)
                    {
                        Console.WriteLine($"CANCELED: ErrorCode={cancellation.ErrorCode}");
                        Console.WriteLine($"CANCELED: ErrorDetails=[{cancellation.ErrorDetails}]");
                        Console.WriteLine($"CANCELED: Did you set the speech resource key and region values?");
                    }
                    break;
                default:
                    break;
            }
        }

        Console.WriteLine("Press any key to exit...");
        Console.ReadKey();
    }
}

その他のテキスト読み上げサンプルは GitHub にあります。

カスタム エンドポイントを使用する

カスタム エンドポイントの機能は、テキスト読み上げ要求に使用される標準のエンドポイントと同じです。

1 つ違うのは、Speech SDK でカスタム音声を使うには、EndpointId を指定する必要があることです。 テキスト読み上げのクイックスタートから始めて、EndpointIdSpeechSynthesisVoiceName でコードを更新できます。

var speechConfig = SpeechConfig.FromSubscription(speechKey, speechRegion);     
speechConfig.SpeechSynthesisVoiceName = "YourCustomVoiceName";
speechConfig.EndpointId = "YourEndpointId";

音声合成マークアップ言語 (SSML) でカスタム音声を使うには、音声名としてモデル名を指定します。 この例では、YourCustomVoiceName 音声を使用します。

<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US">
    <voice name="YourCustomVoiceName">
        This is the text that is spoken. 
    </voice>
</speak>

コンテナーを実行して使用する

音声コンテナーは、WebSocket ベースのクエリ エンドポイント API シリーズを提供します。これには、Speech SDK および Speech CLI を介してアクセスします。 既定では、Speech SDK と Speech CLI ではパブリック音声サービスが使用されます。 コンテナーを使用するには、初期化方法を変更する必要があります。 キーとリージョンではなく、コンテナー ホスト URL を使用します。

音声コンテナーの詳細については、「Docker を使用して Speech コンテナーをインストールして実行する」を参照してください。

リファレンス ドキュメント | パッケージ (NuGet) | GitHub のその他のサンプル

この攻略ガイドでは、テキスト読み上げ合成を行うための一般的な設計パターンについて説明します。

次の領域の詳細については、「テキスト読み上げとは」を参照してください。

  • インメモリ ストリームとしての応答の取得。
  • 出力のサンプル レートとビット レートのカスタマイズ。
  • 音声合成マークアップ言語 (SSML) を使用した合成要求の送信。
  • ニューラル音声の使用。
  • イベントのサブスクライブと、結果に対するアクションの実行。

合成言語と音声を選択する

Speech サービスのテキスト読み上げ機能では、400 を超える音声と、140 を超える言語およびバリアントがサポートされています。 サポートされているテキスト読み上げロケールの完全な一覧に関するページを参照するか、それらを [音声ギャラリー] で試してみてください。

入力テキストに合わせて SpeechConfig の言語または音声を指定し、必要な音声を使用します。 次のコード スニペットは、この手法のしくみを示しています:

void synthesizeSpeech()
{
    auto speechConfig = SpeechConfig::FromSubscription("YourSpeechKey", "YourSpeechRegion");
    // Set either the `SpeechSynthesisVoiceName` or `SpeechSynthesisLanguage`.
    speechConfig->SetSpeechSynthesisLanguage("en-US"); 
    speechConfig->SetSpeechSynthesisVoiceName("en-US-AvaMultilingualNeural");
}

すべてのニューラル音声は多言語で、独自の言語と英語で流暢に読み上げます。 たとえば、英語の入力テキストが "I'm excited to try text to speech" で、es-ES-ElviraNeural を設定した場合、テキストはスペイン語アクセントの英語で読み上げられます。

入力テキストの言語が音声で読み上げられない場合、Speech サービスでは合成された音声が作成されません。 サポートされているニューラル音声の完全な一覧については、「Azure Cognitive Service for Speech サービスの言語と音声のサポート」を参照してください。

Note

既定の音声は、Voice List API からロケールに従って返される最初の音声です。

読み上げられる音声は、次の優先順序で決定されます。

  • SpeechSynthesisVoiceName または SpeechSynthesisLanguage を設定していない場合、en-US の既定の音声が読み上げられます。
  • SpeechSynthesisLanguage のみを設定した場合、指定したロケールの既定の音声が読み上げられます。
  • SpeechSynthesisVoiceNameSpeechSynthesisLanguage の両方を設定した場合、SpeechSynthesisLanguage の設定は無視されます。 SpeechSynthesisVoiceName を使用して指定した音声が読み上げられます。
  • 音声要素を音声合成マークアップ言語 (SSML) で設定した場合、SpeechSynthesisVoiceNameSpeechSynthesisLanguage の設定は無視されます。

音声をファイルに合成する

SpeechSynthesizer オブジェクトを作成します。 このオブジェクトにより、テキスト読み上げ変換と、スピーカー、ファイル、またはその他の出力ストリームへの出力が実行されます。 SpeechSynthesizer は、パラメーターとして以下を受け取ります。

  • 前の手順で作成した SpeechConfig オブジェクト。
  • 出力結果をどのように処理する必要があるかを指定している AudioConfig オブジェクト。
  1. FromWavFileOutput() 関数を使用して .wav ファイルに出力を自動的に書き込む AudioConfig インスタンスを作成します:

    void synthesizeSpeech()
    {
        auto speechConfig = SpeechConfig::FromSubscription("YourSpeechKey", "YourSpeechRegion");
        auto audioConfig = AudioConfig::FromWavFileOutput("path/to/write/file.wav");
    }
    
  2. SpeechSynthesizer インスタンスをインスタンス化します。 speechConfig オブジェクトと audioConfig オブジェクトをパラメーターとして渡します。 音声を合成してファイルに書き込むには、文字列で SpeakTextAsync() を実行します。

    void synthesizeSpeech()
    {
        auto speechConfig = SpeechConfig::FromSubscription("YourSpeechKey", "YourSpeechRegion");
        auto audioConfig = AudioConfig::FromWavFileOutput("path/to/write/file.wav");
        auto speechSynthesizer = SpeechSynthesizer::FromConfig(speechConfig, audioConfig);
        auto result = speechSynthesizer->SpeakTextAsync("A simple test to write to a file.").get();
    }
    

プログラムを実行すると、合成された .wav ファイルが作成され、指定した場所に書き込まれます。 この結果は、最も基本的な使用方法の好例です。 次に、カスタム シナリオに対応できるように、出力をカスタマイズし、出力応答をインメモリ ストリームとして処理することができます。

スピーカー出力に合成する

合成された音声をスピーカーなどの現在アクティブな出力デバイスに出力するには、SpeechSynthesizer インスタンスの作成時に AudioConfig パラメーターを省略します。 次に例を示します。

void synthesizeSpeech()
{
    auto speechConfig = SpeechConfig::FromSubscription("YourSpeechKey", "YourSpeechRegion");
    auto speechSynthesizer = SpeechSynthesizer::FromConfig(speechConfig);
    auto result = speechSynthesizer->SpeakTextAsync("I'm excited to try text to speech").get();
}

結果をインメモリ ストリームとして取得する

結果として得られたオーディオ データは、ファイルに直接書き込むのではなく、インメモリ ストリームとして使用できます。 インメモリ ストリームを使用すると、カスタム動作を構築できます:

  • 結果として得られるバイト配列を、カスタム ダウンストリーム サービス向けのシーク可能なストリームとして抽象化します。
  • 結果を他の API やサービスと統合します。
  • オーディオ データの変更、カスタム .wav ヘッダーの記述、関連するタスクの実行を行います。

前の例にこの変更を加えることができます。 まず、AudioConfig ブロックを削除します。これは、この時点から先は出力動作を手動で管理して制御を強化するためです。 SpeechSynthesizer コンストラクターの AudioConfigNULL を渡します。

Note

前出のスピーカー出力の例で行ったように省略するのではなく、AudioConfigNULL を渡すと、既定では、現在のアクティブな出力デバイスではオーディオが再生されなくなります。

結果を SpeechSynthesisResult 変数に保存します。 GetAudioData ゲッターからは、出力データの byte [] インスタンスが返されます。 この byte [] インスタンスを手動で操作することも、AudioDataStream クラスを使用してインメモリ ストリームを管理することもできます。

この例では、AudioDataStream.FromResult() 静的関数を使用して、結果からストリームを取得します:

void synthesizeSpeech()
{
    auto speechConfig = SpeechConfig::FromSubscription("YourSpeechKey", "YourSpeechRegion");
    auto speechSynthesizer = SpeechSynthesizer::FromConfig(speechConfig);

    auto result = speechSynthesizer->SpeakTextAsync("Getting the response as an in-memory stream.").get();
    auto stream = AudioDataStream::FromResult(result);
}

この時点で、結果として得られた stream オブジェクトを使用して、任意のカスタム動作を実装できます。

オーディオ形式をカスタマイズする

以下を含むオーディオ出力属性をカスタマイズできます。

  • オーディオ ファイルの種類
  • サンプル速度
  • ビット深度

オーディオ形式を変更するには、SpeechConfig オブジェクトで SetSpeechSynthesisOutputFormat() 関数を使用します。 この関数では、SpeechSynthesisOutputFormat 型の enum インスタンスが必要です。 enum を使用して出力形式を選択します。 使用可能な形式については、「オーディオ形式の一覧」を参照してください。

要件に応じて、異なるファイルの種類に向けたさまざまなオプションがあります。 定義上、Raw24Khz16BitMonoPcm のような未加工の形式には、オーディオ ヘッダーが含まれていません。 未加工の形式は、次のいずれかの状況でのみ使用してください。

  • ダウンストリームの実装で未加工のビットストリームをデコードできることがわかっている。
  • ビット深度、サンプル レート、チャネル数のような要因に基づいて、ヘッダーを手動で構築する予定である。

この例では、SpeechConfig オブジェクトに SpeechSynthesisOutputFormat を設定して、忠実度が高い RIFF 形式 Riff24Khz16BitMonoPcm を指定しています。 前のセクションの例と同様に、AudioDataStream を使用して結果のインメモリ ストリームを取得し、それをファイルに書き込みます。

void synthesizeSpeech()
{
    auto speechConfig = SpeechConfig::FromSubscription("YourSpeechKey", "YourSpeechRegion");
    speechConfig->SetSpeechSynthesisOutputFormat(SpeechSynthesisOutputFormat::Riff24Khz16BitMonoPcm);

    auto speechSynthesizer = SpeechSynthesizer::FromConfig(speechConfig);
    auto result = speechSynthesizer->SpeakTextAsync("A simple test to write to a file.").get();

    auto stream = AudioDataStream::FromResult(result);
    stream->SaveToWavFileAsync("path/to/write/file.wav").get();
}

プログラムを実行すると、指定したパスに .wav ファイルが書き込まれます。

SSML を使用して音声の特徴をカスタマイズする

SSML を使用すると、XML スキーマから要求を送信して、テキスト読み上げ出力でのピッチ、発音、読み上げ速度、ボリューム、その他の面を微調整できます。 このセクションでは、音声を変更する例を示します。 詳しくは、「音声合成マークアップ言語の概要」をご覧ください。

SSML を使用したカスタマイズを開始するには、音声を切り替える小規模変更を加えます。

  1. ルート プロジェクト ディレクトリに SSML 構成用の新しい XML ファイルを作成します。

    <speak version="1.0" xmlns="https://www.w3.org/2001/10/synthesis" xml:lang="en-US">
      <voice name="en-US-AvaMultilingualNeural">
        When you're on the freeway, it's a good idea to use a GPS.
      </voice>
    </speak>
    

    この例では、ファイルは ssml.xmlです。 ルート要素は常に <speak> です。 テキストを <voice> 要素にラップすると、name パラメーターを使用して音声を変更できます。 サポートされているニューラル音声の一覧については、「サポートされている言語」を参照してください。

  2. XML ファイルを参照するように音声合成要求を変更する必要があります。 要求はほぼ同じです。 SpeakTextAsync() 関数を使用する代わりに、SpeakSsmlAsync() を使用します。 この関数は XML 文字列を必要とします。 まず、SSML 構成を文字列として読み込みます。 この時点からは、結果のオブジェクトは前の例とまったく同じです。

    void synthesizeSpeech()
    {
        auto speechConfig = SpeechConfig::FromSubscription("YourSpeechKey", "YourSpeechRegion");
        auto speechSynthesizer = SpeechSynthesizer::FromConfig(speechConfig);
    
        std::ifstream file("./ssml.xml");
        std::string ssml, line;
        while (std::getline(file, line))
        {
            ssml += line;
            ssml.push_back('\n');
        }
        auto result = speechSynthesizer->SpeakSsmlAsync(ssml).get();
    
        auto stream = AudioDataStream::FromResult(result);
        stream->SaveToWavFileAsync("path/to/write/file.wav").get();
    }
    

Note

SSML を使用せずに音声を変更するには、SpeechConfig.SetSpeechSynthesisVoiceName("en-US-AndrewMultilingualNeural") を使用して SpeechConfig のプロパティを設定します。

シンセサイザー イベントをサブスクライブする

テキスト読み上げ処理と結果に関する、より多くの分析情報が必要になる場合があります。 たとえば、シンセサイザーが開始および停止したときにそれを知りたい場合や、合成中に発生した他のイベントについて知りたい場合があります。

テキスト読み上げに SpeechSynthesizer を使用している間は、次の表のイベントをサブスクライブできます。

Event 説明 使用事例
BookmarkReached ブックマークに到達したことを通知します。 ブックマーク到達イベントをトリガーするには、SSMLbookmark 要素が必要です。 このイベントは、合成の開始から bookmark 要素までの間の出力オーディオの経過時間を報告します。 イベントの Text プロパティは、ブックマークの mark 属性で設定した文字列値です。 bookmark 要素は読み上げられません。 bookmark 要素を使用すると、SSML にカスタム マーカーを挿入して、オーディオ ストリーム内の各マーカーのオフセットを取得できます。 bookmark 要素は、テキストまたはタグのシーケンス内の特定の場所を参照するために使用できます。
SynthesisCanceled 音声合成が取り消されたことを通知します。 合成が取り消されたときに、それを確認できます。
SynthesisCompleted 音声合成が完了したことを通知します。 合成が完了したときに、それを確認できます。
SynthesisStarted 音声合成が開始したことを通知します。 合成が開始したときに、それを確認できます。
Synthesizing 音声合成が進行中であることを通知します。 このイベントは、SDK が Speech サービスからオーディオ チャンクを受信するたびに発生します。 合成が進行中のときに、それを確認できます。
VisemeReceived 口形素イベントが受信されたことを通知します。 観察された発話における主要な姿勢を表すために、口形素がよく使用されます。 主要な姿勢には、特定の音素を生成するときの唇、顎、舌の位置などが含まれます。 口形素を使用して、スピーチ音声の再生に伴って、キャラクターに顔のアニメーションを付けることができます。
WordBoundary 単語境界が受信されたことを通知します。 このイベントは、新しい音声、句読点、文それぞれの先頭で発生します。 イベントによって、出力音声の開始から現在の単語までのタイム オフセット (ティック単位) が報告されます。 このイベントは、読み上げられようとしている単語の直前に、入力テキスト (または SSML) の文字位置も報告します。 このイベントは、一般にテキストおよび対応するオーディオの相対位置を取得するために使用されます。 新しい単語について知り、タイミングに基づいてアクションを実行できます。 たとえば、単語を読み上げ時に強調表示するタイミングと時間を決定するのに役立つ情報を取得できます。

Note

イベントは、出力オーディオ データが使用可能になると発生します。これは、出力デバイスへの再生よりも高速です。 呼び出し元は、ストリーミングと実際の時間を適切に同期する必要があります。

音声合成のためにイベントをサブスクライブする方法を示す例を次に示します。 クイックスタートの手順に従うことができますが、その main.cpp ファイルの内容を次のコードに置き換えます:

#include <iostream> 
#include <stdlib.h>
#include <speechapi_cxx.h>

using namespace Microsoft::CognitiveServices::Speech;
using namespace Microsoft::CognitiveServices::Speech::Audio;

std::string getEnvironmentVariable(const char* name);

int main()
{
    // This example requires environment variables named "SPEECH_KEY" and "SPEECH_REGION"
    auto speechKey = getEnvironmentVariable("SPEECH_KEY");
    auto speechRegion = getEnvironmentVariable("SPEECH_REGION");

    if ((size(speechKey) == 0) || (size(speechRegion) == 0)) {
        std::cout << "Please set both SPEECH_KEY and SPEECH_REGION environment variables." << std::endl;
        return -1;
    }

    auto speechConfig = SpeechConfig::FromSubscription(speechKey, speechRegion);

    // Required for WordBoundary event sentences.
    speechConfig->SetProperty(PropertyId::SpeechServiceResponse_RequestSentenceBoundary, "true");

    const auto ssml = R"(<speak version='1.0' xml:lang='en-US' xmlns='http://www.w3.org/2001/10/synthesis' xmlns:mstts='http://www.w3.org/2001/mstts'>
        <voice name = 'en-US-AvaMultilingualNeural'>
            <mstts:viseme type = 'redlips_front' />
            The rainbow has seven colors : <bookmark mark = 'colors_list_begin' />Red, orange, yellow, green, blue, indigo, and violet.<bookmark mark = 'colors_list_end' />.
        </voice>
        </speak>)";

    auto speechSynthesizer = SpeechSynthesizer::FromConfig(speechConfig);

    // Subscribe to events

    speechSynthesizer->BookmarkReached += [](const SpeechSynthesisBookmarkEventArgs& e)
    {
        std::cout << "Bookmark reached. "
            << "\r\n\tAudioOffset: " << round(e.AudioOffset / 10000) << "ms"
            << "\r\n\tText: " << e.Text << std::endl;
    };

    speechSynthesizer->SynthesisCanceled += [](const SpeechSynthesisEventArgs& e)
    {
        std::cout << "SynthesisCanceled event" << std::endl;
    };

    speechSynthesizer->SynthesisCompleted += [](const SpeechSynthesisEventArgs& e)
    {
        auto audioDuration = std::chrono::duration_cast<std::chrono::milliseconds>(e.Result->AudioDuration).count();

        std::cout << "SynthesisCompleted event:"
            << "\r\n\tAudioData: " << e.Result->GetAudioData()->size() << "bytes"
            << "\r\n\tAudioDuration: " << audioDuration << std::endl;
    };

    speechSynthesizer->SynthesisStarted += [](const SpeechSynthesisEventArgs& e)
    {
        std::cout << "SynthesisStarted event" << std::endl;
    };

    speechSynthesizer->Synthesizing += [](const SpeechSynthesisEventArgs& e)
    {
        std::cout << "Synthesizing event:"
            << "\r\n\tAudioData: " << e.Result->GetAudioData()->size() << "bytes" << std::endl;
    };

    speechSynthesizer->VisemeReceived += [](const SpeechSynthesisVisemeEventArgs& e)
    {
        std::cout << "VisemeReceived event:"
            << "\r\n\tAudioOffset: " << round(e.AudioOffset / 10000) << "ms"
            << "\r\n\tVisemeId: " << e.VisemeId << std::endl;
    };

    speechSynthesizer->WordBoundary += [](const SpeechSynthesisWordBoundaryEventArgs& e)
    {
        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(e.Duration).count();
        
        auto boundaryType = "";
        switch (e.BoundaryType) {
        case SpeechSynthesisBoundaryType::Punctuation:
            boundaryType = "Punctuation";
            break;
        case SpeechSynthesisBoundaryType::Sentence:
            boundaryType = "Sentence";
            break;
        case SpeechSynthesisBoundaryType::Word:
            boundaryType = "Word";
            break;
        }

        std::cout << "WordBoundary event:"
            // Word, Punctuation, or Sentence
            << "\r\n\tBoundaryType: " << boundaryType
            << "\r\n\tAudioOffset: " << round(e.AudioOffset / 10000) << "ms"
            << "\r\n\tDuration: " << duration
            << "\r\n\tText: \"" << e.Text << "\""
            << "\r\n\tTextOffset: " << e.TextOffset
            << "\r\n\tWordLength: " << e.WordLength << std::endl;
    };

    auto result = speechSynthesizer->SpeakSsmlAsync(ssml).get();

    // Checks result.
    if (result->Reason == ResultReason::SynthesizingAudioCompleted)
    {
        std::cout << "SynthesizingAudioCompleted result" << std::endl;
    }
    else if (result->Reason == ResultReason::Canceled)
    {
        auto cancellation = SpeechSynthesisCancellationDetails::FromResult(result);
        std::cout << "CANCELED: Reason=" << (int)cancellation->Reason << std::endl;

        if (cancellation->Reason == CancellationReason::Error)
        {
            std::cout << "CANCELED: ErrorCode=" << (int)cancellation->ErrorCode << std::endl;
            std::cout << "CANCELED: ErrorDetails=[" << cancellation->ErrorDetails << "]" << std::endl;
            std::cout << "CANCELED: Did you set the speech resource key and region values?" << std::endl;
        }
    }

    std::cout << "Press enter to exit..." << std::endl;
    std::cin.get();
}

std::string getEnvironmentVariable(const char* name)
{
#if defined(_MSC_VER)
    size_t requiredSize = 0;
    (void)getenv_s(&requiredSize, nullptr, 0, name);
    if (requiredSize == 0)
    {
        return "";
    }
    auto buffer = std::make_unique<char[]>(requiredSize);
    (void)getenv_s(&requiredSize, buffer.get(), requiredSize, name);
    return buffer.get();
#else
    auto value = getenv(name);
    return value ? value : "";
#endif
}

その他のテキスト読み上げサンプルは GitHub にあります。

カスタム エンドポイントを使用する

カスタム エンドポイントの機能は、テキスト読み上げ要求に使用される標準のエンドポイントと同じです。

1 つ違うのは、Speech SDK でカスタム音声を使うには、EndpointId を指定する必要があることです。 テキスト読み上げのクイックスタートから始めて、EndpointIdSpeechSynthesisVoiceName でコードを更新できます。

auto speechConfig = SpeechConfig::FromSubscription(speechKey, speechRegion);
speechConfig->SetSpeechSynthesisVoiceName("YourCustomVoiceName");
speechConfig->SetEndpointId("YourEndpointId");

音声合成マークアップ言語 (SSML) でカスタム音声を使うには、音声名としてモデル名を指定します。 この例では、YourCustomVoiceName 音声を使用します。

<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US">
    <voice name="YourCustomVoiceName">
        This is the text that is spoken. 
    </voice>
</speak>

コンテナーを実行して使用する

音声コンテナーは、WebSocket ベースのクエリ エンドポイント API シリーズを提供します。これには、Speech SDK および Speech CLI を介してアクセスします。 既定では、Speech SDK と Speech CLI ではパブリック音声サービスが使用されます。 コンテナーを使用するには、初期化方法を変更する必要があります。 キーとリージョンではなく、コンテナー ホスト URL を使用します。

音声コンテナーの詳細については、「Docker を使用して Speech コンテナーをインストールして実行する」を参照してください。

リファレンス ドキュメント | パッケージ (Go) | GitHub のその他のサンプル

この攻略ガイドでは、テキスト読み上げ合成を行うための一般的な設計パターンについて説明します。

次の領域の詳細については、「テキスト読み上げとは」を参照してください。

  • インメモリ ストリームとしての応答の取得。
  • 出力のサンプル レートとビット レートのカスタマイズ。
  • 音声合成マークアップ言語 (SSML) を使用した合成要求の送信。
  • ニューラル音声の使用。
  • イベントのサブスクライブと、結果に対するアクションの実行。

前提条件

Speech SDK のインストール

なんらかの操作を行うには、事前に Speech SDK for Go をインストールしておく必要があります。

スピーカーへのテキスト読み上げ

音声合成を既定のオーディオ出力デバイスに対して実行するには、次のコード サンプルを使用します。 変数 subscriptionregion を、音声キーと場所またはリージョンに置き換えます。 スクリプトを実行すると、入力テキストが既定のスピーカーで読み上げられます。

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
    "time"

    "github.com/Microsoft/cognitive-services-speech-sdk-go/audio"
    "github.com/Microsoft/cognitive-services-speech-sdk-go/common"
    "github.com/Microsoft/cognitive-services-speech-sdk-go/speech"
)

func synthesizeStartedHandler(event speech.SpeechSynthesisEventArgs) {
    defer event.Close()
    fmt.Println("Synthesis started.")
}

func synthesizingHandler(event speech.SpeechSynthesisEventArgs) {
    defer event.Close()
    fmt.Printf("Synthesizing, audio chunk size %d.\n", len(event.Result.AudioData))
}

func synthesizedHandler(event speech.SpeechSynthesisEventArgs) {
    defer event.Close()
    fmt.Printf("Synthesized, audio length %d.\n", len(event.Result.AudioData))
}

func cancelledHandler(event speech.SpeechSynthesisEventArgs) {
    defer event.Close()
    fmt.Println("Received a cancellation.")
}

func main() {
    subscription := "YourSpeechKey"
    region := "YourSpeechRegion"

    audioConfig, err := audio.NewAudioConfigFromDefaultSpeakerOutput()
    if err != nil {
        fmt.Println("Got an error: ", err)
        return
    }
    defer audioConfig.Close()
    speechConfig, err := speech.NewSpeechConfigFromSubscription(subscription, region)
    if err != nil {
        fmt.Println("Got an error: ", err)
        return
    }
    defer speechConfig.Close()
    speechSynthesizer, err := speech.NewSpeechSynthesizerFromConfig(speechConfig, audioConfig)
    if err != nil {
        fmt.Println("Got an error: ", err)
        return
    }
    defer speechSynthesizer.Close()

    speechSynthesizer.SynthesisStarted(synthesizeStartedHandler)
    speechSynthesizer.Synthesizing(synthesizingHandler)
    speechSynthesizer.SynthesisCompleted(synthesizedHandler)
    speechSynthesizer.SynthesisCanceled(cancelledHandler)

    for {
        fmt.Printf("Enter some text that you want to speak, or enter empty text to exit.\n> ")
        text, _ := bufio.NewReader(os.Stdin).ReadString('\n')
        text = strings.TrimSuffix(text, "\n")
        if len(text) == 0 {
            break
        }

        task := speechSynthesizer.SpeakTextAsync(text)
        var outcome speech.SpeechSynthesisOutcome
        select {
        case outcome = <-task:
        case <-time.After(60 * time.Second):
            fmt.Println("Timed out")
            return
        }
        defer outcome.Close()
        if outcome.Error != nil {
            fmt.Println("Got an error: ", outcome.Error)
            return
        }

        if outcome.Result.Reason == common.SynthesizingAudioCompleted {
            fmt.Printf("Speech synthesized to speaker for text [%s].\n", text)
        } else {
            cancellation, _ := speech.NewCancellationDetailsFromSpeechSynthesisResult(outcome.Result)
            fmt.Printf("CANCELED: Reason=%d.\n", cancellation.Reason)

            if cancellation.Reason == common.Error {
                fmt.Printf("CANCELED: ErrorCode=%d\nCANCELED: ErrorDetails=[%s]\nCANCELED: Did you set the speech resource key and region values?\n",
                    cancellation.ErrorCode,
                    cancellation.ErrorDetails)
            }
        }
    }
}

次のコマンドを実行して、GitHub でホストされているコンポーネントにリンクする go.mod ファイルを作成します。

go mod init quickstart
go get github.com/Microsoft/cognitive-services-speech-sdk-go

ここでコードをビルドして実行します。

go build
go run quickstart

これらのクラスの詳細については、SpeechConfig および SpeechSynthesizer のリファレンス ドキュメントを参照してください。

メモリ内ストリームへのテキスト読み上げ

結果として得られたオーディオ データは、ファイルに直接書き込むのではなく、インメモリ ストリームとして使用できます。 インメモリ ストリームを使用すると、カスタム動作を構築できます:

  • 結果として得られるバイト配列を、カスタム ダウンストリーム サービス向けのシーク可能なストリームとして抽象化します。
  • 結果を他の API やサービスと統合します。
  • オーディオ データの変更、カスタム .wav ヘッダーの記述、関連するタスクの実行を行います。

前の例にこの変更を加えることができます。 AudioConfig ブロックを削除します。これは、この時点から先は出力動作を手動で管理して制御を強化するためです。 次に、SpeechSynthesizer コンストラクターの AudioConfignil を渡します。

注意

前出のスピーカー出力の例で行ったように省略するのではなく、AudioConfignil を渡すと、既定では、現在のアクティブな出力デバイスではオーディオが再生されなくなります。

結果を SpeechSynthesisResult 変数に保存します。 AudioData プロパティでは、出力データの []byte インスタンスが返されます。 この []byte インスタンスを手動で操作することも、AudioDataStream クラスを使用してインメモリ ストリームを管理することもできます。 この例では、NewAudioDataStreamFromSpeechSynthesisResult() 静的関数を使用して、結果からストリームを取得します。

変数 subscription および region を、音声キーと場所またはリージョンに置き換えます。

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
    "strings"
    "time"

    "github.com/Microsoft/cognitive-services-speech-sdk-go/speech"
)

func synthesizeStartedHandler(event speech.SpeechSynthesisEventArgs) {
    defer event.Close()
    fmt.Println("Synthesis started.")
}

func synthesizingHandler(event speech.SpeechSynthesisEventArgs) {
    defer event.Close()
    fmt.Printf("Synthesizing, audio chunk size %d.\n", len(event.Result.AudioData))
}

func synthesizedHandler(event speech.SpeechSynthesisEventArgs) {
    defer event.Close()
    fmt.Printf("Synthesized, audio length %d.\n", len(event.Result.AudioData))
}

func cancelledHandler(event speech.SpeechSynthesisEventArgs) {
    defer event.Close()
    fmt.Println("Received a cancellation.")
}

func main() {
    subscription := "YourSpeechKey"
    region := "YourSpeechRegion"

    speechConfig, err := speech.NewSpeechConfigFromSubscription(subscription, region)
    if err != nil {
        fmt.Println("Got an error: ", err)
        return
    }
    defer speechConfig.Close()
    speechSynthesizer, err := speech.NewSpeechSynthesizerFromConfig(speechConfig, nil)
    if err != nil {
        fmt.Println("Got an error: ", err)
        return
    }
    defer speechSynthesizer.Close()

    speechSynthesizer.SynthesisStarted(synthesizeStartedHandler)
    speechSynthesizer.Synthesizing(synthesizingHandler)
    speechSynthesizer.SynthesisCompleted(synthesizedHandler)
    speechSynthesizer.SynthesisCanceled(cancelledHandler)

    for {
        fmt.Printf("Enter some text that you want to speak, or enter empty text to exit.\n> ")
        text, _ := bufio.NewReader(os.Stdin).ReadString('\n')
        text = strings.TrimSuffix(text, "\n")
        if len(text) == 0 {
            break
        }

        // StartSpeakingTextAsync sends the result to channel when the synthesis starts.
        task := speechSynthesizer.StartSpeakingTextAsync(text)
        var outcome speech.SpeechSynthesisOutcome
        select {
        case outcome = <-task:
        case <-time.After(60 * time.Second):
            fmt.Println("Timed out")
            return
        }
        defer outcome.Close()
        if outcome.Error != nil {
            fmt.Println("Got an error: ", outcome.Error)
            return
        }

        // In most cases, we want to streaming receive the audio to lower the latency.
        // We can use AudioDataStream to do so.
        stream, err := speech.NewAudioDataStreamFromSpeechSynthesisResult(outcome.Result)
        defer stream.Close()
        if err != nil {
            fmt.Println("Got an error: ", err)
            return
        }

        var all_audio []byte
        audio_chunk := make([]byte, 2048)
        for {
            n, err := stream.Read(audio_chunk)

            if err == io.EOF {
                break
            }

            all_audio = append(all_audio, audio_chunk[:n]...)
        }

        fmt.Printf("Read [%d] bytes from audio data stream.\n", len(all_audio))
    }
}

次のコマンドを実行して、GitHub でホストされているコンポーネントにリンクする go.mod ファイルを作成します。

go mod init quickstart
go get github.com/Microsoft/cognitive-services-speech-sdk-go

ここでコードをビルドして実行します。

go build
go run quickstart

これらのクラスの詳細については、SpeechConfig および SpeechSynthesizer のリファレンス ドキュメントを参照してください。

合成言語と音声を選択する

Speech サービスのテキスト読み上げ機能では、400 を超える音声と、140 を超える言語およびバリアントがサポートされています。 すべてのリストを入手することも、音声ギャラリーでそれらを試すこともできます。

入力テキストに合わせて SpeechConfig の言語または音声を指定し、必要な音声を使用します:

speechConfig, err := speech.NewSpeechConfigFromSubscription(key, region)
if err != nil {
    fmt.Println("Got an error: ", err)
    return
}
defer speechConfig.Close()

speechConfig.SetSpeechSynthesisLanguage("en-US")
speechConfig.SetSpeechSynthesisVoiceName("en-US-AvaMultilingualNeural")

すべてのニューラル音声は多言語で、独自の言語と英語で流暢に読み上げます。 たとえば、英語の入力テキストが "I'm excited to try text to speech" で、es-ES-ElviraNeural を設定した場合、テキストはスペイン語アクセントの英語で読み上げられます。

入力テキストの言語が音声で読み上げられない場合、Speech サービスでは合成された音声が作成されません。 サポートされているニューラル音声の完全な一覧については、「Azure Cognitive Service for Speech サービスの言語と音声のサポート」を参照してください。

Note

既定の音声は、Voice List API からロケールに従って返される最初の音声です。

読み上げられる音声は、次の優先順序で決定されます。

  • SpeechSynthesisVoiceName または SpeechSynthesisLanguage を設定していない場合、en-US の既定の音声が読み上げられます。
  • SpeechSynthesisLanguage のみを設定した場合、指定したロケールの既定の音声が読み上げられます。
  • SpeechSynthesisVoiceNameSpeechSynthesisLanguage の両方を設定した場合、SpeechSynthesisLanguage の設定は無視されます。 SpeechSynthesisVoiceName を使用して指定した音声が読み上げられます。
  • 音声要素を音声合成マークアップ言語 (SSML) で設定した場合、SpeechSynthesisVoiceNameSpeechSynthesisLanguage の設定は無視されます。

SSML を使用して音声の特徴をカスタマイズする

音声合成マークアップ言語 (SSML) を使用すると、XML スキーマから要求を送信して、テキスト読み上げ出力におけるピッチ、発音、読み上げ速度、ボリュームなどを微調整できます。 このセクションでは、音声を変更する例を示します。 詳しくは、「音声合成マークアップ言語の概要」をご覧ください。

SSML を使用したカスタマイズを開始するには、音声を切り替える小規模変更を加えます。

まず、ルート プロジェクト ディレクトリに SSML 構成用の新しい XML ファイルを作成します。 この例では ssml.xml です。 ルート要素は常に <speak> です。 テキストを <voice> 要素にラップすると、name パラメーターを使用して音声を変更できます。 サポートされているニューラル音声の一覧については、「サポートされている言語」を参照してください。

<speak version="1.0" xmlns="https://www.w3.org/2001/10/synthesis" xml:lang="en-US">
  <voice name="en-US-AvaMultilingualNeural">
    When you're on the freeway, it's a good idea to use a GPS.
  </voice>
</speak>

次に、XML ファイルを参照するように音声合成要求を変更する必要があります。 要求はほとんど同じですが、SpeakTextAsync() 関数を使用する代わりに、SpeakSsmlAsync() を使用します。 この関数には XML 文字列が必要なので、最初に SSML 構成を文字列として読み込みます。 この時点からは、結果のオブジェクトは前の例とまったく同じです。

Note

SSML を使用せずに音声を設定するには、speechConfig.SetSpeechSynthesisVoiceName("en-US-AvaMultilingualNeural") を使って SpeechConfig のプロパティを設定できます。

シンセサイザー イベントをサブスクライブする

テキスト読み上げ処理と結果に関する、より多くの分析情報が必要になる場合があります。 たとえば、シンセサイザーが開始および停止したときにそれを知りたい場合や、合成中に発生した他のイベントについて知りたい場合があります。

テキスト読み上げに SpeechSynthesizer を使用している間は、次の表のイベントをサブスクライブできます。

Event 説明 使用事例
BookmarkReached ブックマークに到達したことを通知します。 ブックマーク到達イベントをトリガーするには、SSMLbookmark 要素が必要です。 このイベントは、合成の開始から bookmark 要素までの間の出力オーディオの経過時間を報告します。 イベントの Text プロパティは、ブックマークの mark 属性で設定した文字列値です。 bookmark 要素は読み上げられません。 bookmark 要素を使用すると、SSML にカスタム マーカーを挿入して、オーディオ ストリーム内の各マーカーのオフセットを取得できます。 bookmark 要素は、テキストまたはタグのシーケンス内の特定の場所を参照するために使用できます。
SynthesisCanceled 音声合成が取り消されたことを通知します。 合成が取り消されたときに、それを確認できます。
SynthesisCompleted 音声合成が完了したことを通知します。 合成が完了したときに、それを確認できます。
SynthesisStarted 音声合成が開始したことを通知します。 合成が開始したときに、それを確認できます。
Synthesizing 音声合成が進行中であることを通知します。 このイベントは、SDK が Speech サービスからオーディオ チャンクを受信するたびに発生します。 合成が進行中のときに、それを確認できます。
VisemeReceived 口形素イベントが受信されたことを通知します。 観察された発話における主要な姿勢を表すために、口形素がよく使用されます。 主要な姿勢には、特定の音素を生成するときの唇、顎、舌の位置などが含まれます。 口形素を使用して、スピーチ音声の再生に伴って、キャラクターに顔のアニメーションを付けることができます。
WordBoundary 単語境界が受信されたことを通知します。 このイベントは、新しい音声、句読点、文それぞれの先頭で発生します。 イベントによって、出力音声の開始から現在の単語までのタイム オフセット (ティック単位) が報告されます。 このイベントは、読み上げられようとしている単語の直前に、入力テキスト (または SSML) の文字位置も報告します。 このイベントは、一般にテキストおよび対応するオーディオの相対位置を取得するために使用されます。 新しい単語について知り、タイミングに基づいてアクションを実行できます。 たとえば、単語を読み上げ時に強調表示するタイミングと時間を決定するのに役立つ情報を取得できます。

Note

イベントは、出力オーディオ データが使用可能になると発生します。これは、出力デバイスへの再生よりも高速です。 呼び出し元は、ストリーミングと実際の時間を適切に同期する必要があります。

音声合成のためにイベントをサブスクライブする方法を示す例を次に示します。 クイックスタートの手順に従うことができますが、その speech-synthesis.go ファイルの内容を次の Go コードに置き換えます:

package main

import (
    "fmt"
    "os"
    "time"

    "github.com/Microsoft/cognitive-services-speech-sdk-go/audio"
    "github.com/Microsoft/cognitive-services-speech-sdk-go/common"
    "github.com/Microsoft/cognitive-services-speech-sdk-go/speech"
)

func bookmarkReachedHandler(event speech.SpeechSynthesisBookmarkEventArgs) {
    defer event.Close()
    fmt.Println("BookmarkReached event")
}

func synthesisCanceledHandler(event speech.SpeechSynthesisEventArgs) {
    defer event.Close()
    fmt.Println("SynthesisCanceled event")
}

func synthesisCompletedHandler(event speech.SpeechSynthesisEventArgs) {
    defer event.Close()
    fmt.Println("SynthesisCompleted event")
    fmt.Printf("\tAudioData: %d bytes\n", len(event.Result.AudioData))
    fmt.Printf("\tAudioDuration: %d\n", event.Result.AudioDuration)
}

func synthesisStartedHandler(event speech.SpeechSynthesisEventArgs) {
    defer event.Close()
    fmt.Println("SynthesisStarted event")
}

func synthesizingHandler(event speech.SpeechSynthesisEventArgs) {
    defer event.Close()
    fmt.Println("Synthesizing event")
    fmt.Printf("\tAudioData %d bytes\n", len(event.Result.AudioData))
}

func visemeReceivedHandler(event speech.SpeechSynthesisVisemeEventArgs) {
    defer event.Close()
    fmt.Println("VisemeReceived event")
    fmt.Printf("\tAudioOffset: %dms\n", (event.AudioOffset+5000)/10000)
    fmt.Printf("\tVisemeID %d\n", event.VisemeID)
}

func wordBoundaryHandler(event speech.SpeechSynthesisWordBoundaryEventArgs) {
    defer event.Close()
    boundaryType := ""
    switch event.BoundaryType {
    case 0:
        boundaryType = "Word"
    case 1:
        boundaryType = "Punctuation"
    case 2:
        boundaryType = "Sentence"
    }
    fmt.Println("WordBoundary event")
    fmt.Printf("\tBoundaryType %v\n", boundaryType)
    fmt.Printf("\tAudioOffset: %dms\n", (event.AudioOffset+5000)/10000)
    fmt.Printf("\tDuration %d\n", event.Duration)
    fmt.Printf("\tText %s\n", event.Text)
    fmt.Printf("\tTextOffset %d\n", event.TextOffset)
    fmt.Printf("\tWordLength %d\n", event.WordLength)
}

func main() {
    // This example requires environment variables named "SPEECH_KEY" and "SPEECH_REGION"
    speechKey := os.Getenv("SPEECH_KEY")
    speechRegion := os.Getenv("SPEECH_REGION")

    audioConfig, err := audio.NewAudioConfigFromDefaultSpeakerOutput()
    if err != nil {
        fmt.Println("Got an error: ", err)
        return
    }
    defer audioConfig.Close()
    speechConfig, err := speech.NewSpeechConfigFromSubscription(speechKey, speechRegion)
    if err != nil {
        fmt.Println("Got an error: ", err)
        return
    }
    defer speechConfig.Close()

    // Required for WordBoundary event sentences.
    speechConfig.SetProperty(common.SpeechServiceResponseRequestSentenceBoundary, "true")

    speechSynthesizer, err := speech.NewSpeechSynthesizerFromConfig(speechConfig, audioConfig)
    if err != nil {
        fmt.Println("Got an error: ", err)
        return
    }
    defer speechSynthesizer.Close()

    speechSynthesizer.BookmarkReached(bookmarkReachedHandler)
    speechSynthesizer.SynthesisCanceled(synthesisCanceledHandler)
    speechSynthesizer.SynthesisCompleted(synthesisCompletedHandler)
    speechSynthesizer.SynthesisStarted(synthesisStartedHandler)
    speechSynthesizer.Synthesizing(synthesizingHandler)
    speechSynthesizer.VisemeReceived(visemeReceivedHandler)
    speechSynthesizer.WordBoundary(wordBoundaryHandler)

    speechSynthesisVoiceName := "en-US-AvaMultilingualNeural"

    ssml := fmt.Sprintf(`<speak version='1.0' xml:lang='en-US' xmlns='http://www.w3.org/2001/10/synthesis' xmlns:mstts='http://www.w3.org/2001/mstts'>
            <voice name='%s'>
                <mstts:viseme type='redlips_front'/>
                The rainbow has seven colors: <bookmark mark='colors_list_begin'/>Red, orange, yellow, green, blue, indigo, and violet.<bookmark mark='colors_list_end'/>.
            </voice>
        </speak>`, speechSynthesisVoiceName)

    // Synthesize the SSML
    fmt.Printf("SSML to synthesize: \n\t%s\n", ssml)
    task := speechSynthesizer.SpeakSsmlAsync(ssml)

    var outcome speech.SpeechSynthesisOutcome
    select {
    case outcome = <-task:
    case <-time.After(60 * time.Second):
        fmt.Println("Timed out")
        return
    }
    defer outcome.Close()
    if outcome.Error != nil {
        fmt.Println("Got an error: ", outcome.Error)
        return
    }

    if outcome.Result.Reason == common.SynthesizingAudioCompleted {
        fmt.Println("SynthesizingAudioCompleted result")
    } else {
        cancellation, _ := speech.NewCancellationDetailsFromSpeechSynthesisResult(outcome.Result)
        fmt.Printf("CANCELED: Reason=%d.\n", cancellation.Reason)

        if cancellation.Reason == common.Error {
            fmt.Printf("CANCELED: ErrorCode=%d\nCANCELED: ErrorDetails=[%s]\nCANCELED: Did you set the speech resource key and region values?\n",
                cancellation.ErrorCode,
                cancellation.ErrorDetails)
        }
    }
}

その他のテキスト読み上げサンプルは GitHub にあります。

コンテナーを実行して使用する

音声コンテナーは、WebSocket ベースのクエリ エンドポイント API シリーズを提供します。これには、Speech SDK および Speech CLI を介してアクセスします。 既定では、Speech SDK と Speech CLI ではパブリック音声サービスが使用されます。 コンテナーを使用するには、初期化方法を変更する必要があります。 キーとリージョンではなく、コンテナー ホスト URL を使用します。

音声コンテナーの詳細については、「Docker を使用して Speech コンテナーをインストールして実行する」を参照してください。

リファレンス ドキュメント | GitHub のその他のサンプル

この攻略ガイドでは、テキスト読み上げ合成を行うための一般的な設計パターンについて説明します。

次の領域の詳細については、「テキスト読み上げとは」を参照してください。

  • インメモリ ストリームとしての応答の取得。
  • 出力のサンプル レートとビット レートのカスタマイズ。
  • 音声合成マークアップ言語 (SSML) を使用した合成要求の送信。
  • ニューラル音声の使用。
  • イベントのサブスクライブと、結果に対するアクションの実行。

合成言語と音声を選択する

Speech サービスのテキスト読み上げ機能では、400 を超える音声と、140 を超える言語およびバリアントがサポートされています。 すべてのリストを入手することも、音声ギャラリーでそれらを試すこともできます。

入力テキストに合わせて SpeechConfig の言語または音声を指定し、指定された音声を使用します。 次のコード スニペットは、この手法のしくみを示しています:

public static void main(String[] args) {
    SpeechConfig speechConfig = SpeechConfig.fromSubscription("YourSpeechKey", "YourSpeechRegion");
    // Set either the `SpeechSynthesisVoiceName` or `SpeechSynthesisLanguage`.
    speechConfig.setSpeechSynthesisLanguage("en-US"); 
    speechConfig.setSpeechSynthesisVoiceName("en-US-AvaMultilingualNeural");
}

すべてのニューラル音声は多言語で、独自の言語と英語で流暢に読み上げます。 たとえば、英語の入力テキストが "I'm excited to try text to speech" で、es-ES-ElviraNeural を設定した場合、テキストはスペイン語アクセントの英語で読み上げられます。

入力テキストの言語が音声で読み上げられない場合、Speech サービスでは合成された音声が作成されません。 サポートされているニューラル音声の完全な一覧については、「Azure Cognitive Service for Speech サービスの言語と音声のサポート」を参照してください。

Note

既定の音声は、Voice List API からロケールに従って返される最初の音声です。

読み上げられる音声は、次の優先順序で決定されます。

  • SpeechSynthesisVoiceName または SpeechSynthesisLanguage を設定していない場合、en-US の既定の音声が読み上げられます。
  • SpeechSynthesisLanguage のみを設定した場合、指定したロケールの既定の音声が読み上げられます。
  • SpeechSynthesisVoiceNameSpeechSynthesisLanguage の両方を設定した場合、SpeechSynthesisLanguage の設定は無視されます。 SpeechSynthesisVoiceName を使用して指定した音声が読み上げられます。
  • 音声要素を音声合成マークアップ言語 (SSML) で設定した場合、SpeechSynthesisVoiceNameSpeechSynthesisLanguage の設定は無視されます。

音声をファイルに合成する

SpeechSynthesizer オブジェクトを作成します。 このオブジェクトにより、テキスト読み上げ変換と、スピーカー、ファイル、またはその他の出力ストリームへの出力が実行されます。 SpeechSynthesizer は、パラメーターとして以下を受け取ります。

  • 前の手順で作成した SpeechConfig オブジェクト
  • 出力結果をどのように処理する必要があるかを指定している AudioConfig オブジェクト。
  1. fromWavFileOutput() 静的関数を使用して .wav ファイルに出力を自動的に書き込む AudioConfig インスタンスを作成します:

    public static void main(String[] args) {
        SpeechConfig speechConfig = SpeechConfig.fromSubscription("YourSpeechKey", "YourSpeechRegion");
        AudioConfig audioConfig = AudioConfig.fromWavFileOutput("path/to/write/file.wav");
    }
    
  2. SpeechSynthesizer インスタンスをインスタンス化します。 speechConfig オブジェクトと audioConfig オブジェクトをパラメーターとして渡します。 音声を合成してファイルに書き込むには、文字列で SpeakText() を実行します。

    public static void main(String[] args) {
        SpeechConfig speechConfig = SpeechConfig.fromSubscription("YourSpeechKey", "YourSpeechRegion");
        AudioConfig audioConfig = AudioConfig.fromWavFileOutput("path/to/write/file.wav");
    
        SpeechSynthesizer speechSynthesizer = new SpeechSynthesizer(speechConfig, audioConfig);
        speechSynthesizer.SpeakText("I'm excited to try text to speech");
    }
    

プログラムを実行すると、合成された .wav ファイルが作成され、指定した場所に書き込まれます。 この結果は、最も基本的な使用方法の好例です。 次に、カスタム シナリオに対応できるように、出力をカスタマイズし、出力応答をインメモリ ストリームとして処理することができます。

スピーカー出力に合成する

テキスト読み上げ処理と結果に関する、より多くの分析情報が必要になる場合があります。 たとえば、シンセサイザーが開始および停止したときにそれを知りたい場合や、合成中に発生した他のイベントについて知りたい場合があります。

合成された音声をスピーカーなどの現在アクティブな出力デバイスに出力するには、fromDefaultSpeakerOutput() 静的関数を使用して AudioConfig をインスタンス化します。 次に例を示します。

public static void main(String[] args) {
    SpeechConfig speechConfig = SpeechConfig.fromSubscription("YourSpeechKey", "YourSpeechRegion");
    AudioConfig audioConfig = AudioConfig.fromDefaultSpeakerOutput();

    SpeechSynthesizer speechSynthesizer = new SpeechSynthesizer(speechConfig, audioConfig);
    speechSynthesizer.SpeakText("I'm excited to try text to speech");
}

結果をインメモリ ストリームとして取得する

結果として得られたオーディオ データは、ファイルに直接書き込むのではなく、インメモリ ストリームとして使用できます。 インメモリ ストリームを使用すると、カスタム動作を構築できます:

  • 結果として得られるバイト配列を、カスタム ダウンストリーム サービス向けのシーク可能なストリームとして抽象化します。
  • 結果を他の API やサービスと統合します。
  • オーディオ データの変更、カスタム .wav ヘッダーの記述、関連するタスクの実行を行います。

前の例にこの変更を加えることができます。 まず、AudioConfig ブロックを削除します。これは、この時点から先は出力動作を手動で管理して制御を強化するためです。 次に、SpeechSynthesizer コンストラクターの AudioConfignull を渡します。

Note

前出のスピーカー出力の例で行ったように省略するのではなく、AudioConfignull を渡すと、既定では、現在のアクティブな出力デバイスではオーディオが再生されなくなります。

結果を SpeechSynthesisResult 変数に保存します。 SpeechSynthesisResult.getAudioData() 関数からは、出力データの byte [] インスタンスが返されます。 この byte [] インスタンスを手動で操作することも、AudioDataStream クラスを使用してインメモリ ストリームを管理することもできます。

この例では、AudioDataStream.fromResult() 静的関数を使用して、結果からストリームを取得します:

public static void main(String[] args) {
    SpeechConfig speechConfig = SpeechConfig.fromSubscription("YourSpeechKey", "YourSpeechRegion");
    SpeechSynthesizer speechSynthesizer = new SpeechSynthesizer(speechConfig, null);

    SpeechSynthesisResult result = speechSynthesizer.SpeakText("I'm excited to try text to speech");
    AudioDataStream stream = AudioDataStream.fromResult(result);
    System.out.print(stream.getStatus());
}

この時点で、結果として得られた stream オブジェクトを使用して、任意のカスタム動作を実装できます。

オーディオ形式をカスタマイズする

以下を含むオーディオ出力属性をカスタマイズできます。

  • オーディオ ファイルの種類
  • サンプル速度
  • ビット深度

オーディオ形式を変更するには、SpeechConfig オブジェクトで setSpeechSynthesisOutputFormat() 関数を使用します。 この関数では、SpeechSynthesisOutputFormat 型の enum インスタンスが必要です。 enum を使用して出力形式を選択します。 使用可能な形式については、「オーディオ形式の一覧」を参照してください。

要件に応じて、異なるファイルの種類に向けたさまざまなオプションがあります。 定義上、Raw24Khz16BitMonoPcm のような未加工の形式には、オーディオ ヘッダーが含まれていません。 未加工の形式は、次のいずれかの状況でのみ使用してください。

  • ダウンストリームの実装で未加工のビットストリームをデコードできることがわかっている。
  • ビット深度、サンプル レート、チャネル数のような要因に基づいて、ヘッダーを手動で構築する予定である。

この例では、SpeechConfig オブジェクトに SpeechSynthesisOutputFormat を設定して、忠実度が高い RIFF 形式 Riff24Khz16BitMonoPcm を指定しています。 前のセクションの例と同様に、AudioDataStream を使用して結果のインメモリ ストリームを取得し、それをファイルに書き込みます。

public static void main(String[] args) {
    SpeechConfig speechConfig = SpeechConfig.fromSubscription("YourSpeechKey", "YourSpeechRegion");

    // set the output format
    speechConfig.setSpeechSynthesisOutputFormat(SpeechSynthesisOutputFormat.Riff24Khz16BitMonoPcm);

    SpeechSynthesizer speechSynthesizer = new SpeechSynthesizer(speechConfig, null);
    SpeechSynthesisResult result = speechSynthesizer.SpeakText("I'm excited to try text to speech");
    AudioDataStream stream = AudioDataStream.fromResult(result);
    stream.saveToWavFile("path/to/write/file.wav");
}

プログラムを実行すると、指定したパスに .wav ファイルが書き込まれます。

SSML を使用して音声の特徴をカスタマイズする

SSML を使用すると、XML スキーマから要求を送信して、テキスト読み上げ出力でのピッチ、発音、読み上げ速度、ボリューム、その他の面を微調整できます。 このセクションでは、音声を変更する例を示します。 詳しくは、SSML の使い方に関する記事をご覧ください。

SSML を使用したカスタマイズを開始するには、音声を切り替える小規模変更を加えます。

  1. ルート プロジェクト ディレクトリに SSML 構成用の新しい XML ファイルを作成します。

    <speak version="1.0" xmlns="https://www.w3.org/2001/10/synthesis" xml:lang="en-US">
      <voice name="en-US-AvaMultilingualNeural">
        When you're on the freeway, it's a good idea to use a GPS.
      </voice>
    </speak>
    

    この例では、ファイルは ssml.xmlです。 ルート要素は常に <speak> です。 テキストを <voice> 要素にラップすると、name パラメーターを使用して音声を変更できます。 サポートされているニューラル音声の一覧については、「サポートされている言語」を参照してください。

  2. XML ファイルを参照するように音声合成要求を変更する必要があります。 要求はほぼ同じです。 SpeakText() 関数を使用する代わりに、SpeakSsml() を使用します。 この関数には XML 文字列が必要なので、最初に、XML ファイルを読み込んでそれを文字列として返す関数を作成します:

    private static String xmlToString(String filePath) {
        File file = new File(filePath);
        StringBuilder fileContents = new StringBuilder((int)file.length());
    
        try (Scanner scanner = new Scanner(file)) {
            while(scanner.hasNextLine()) {
                fileContents.append(scanner.nextLine() + System.lineSeparator());
            }
            return fileContents.toString().trim();
        } catch (FileNotFoundException ex) {
            return "File not found.";
        }
    }
    

    この時点では、結果のオブジェクトは前の例とまったく同じです:

    public static void main(String[] args) {
        SpeechConfig speechConfig = SpeechConfig.fromSubscription("YourSpeechKey", "YourSpeechRegion");
        SpeechSynthesizer speechSynthesizer = new SpeechSynthesizer(speechConfig, null);
    
        String ssml = xmlToString("ssml.xml");
        SpeechSynthesisResult result = speechSynthesizer.SpeakSsml(ssml);
        AudioDataStream stream = AudioDataStream.fromResult(result);
        stream.saveToWavFile("path/to/write/file.wav");
    }
    

Note

SSML を使用せずに音声を変更するには、SpeechConfig.setSpeechSynthesisVoiceName("en-US-AvaMultilingualNeural"); を使用して SpeechConfig のプロパティを設定します。

シンセサイザー イベントをサブスクライブする

テキスト読み上げ処理と結果に関する、より多くの分析情報が必要になる場合があります。 たとえば、シンセサイザーが開始および停止したときにそれを知りたい場合や、合成中に発生した他のイベントについて知りたい場合があります。

テキスト読み上げに SpeechSynthesizer を使用している間は、次の表のイベントをサブスクライブできます。

Event 説明 使用事例
BookmarkReached ブックマークに到達したことを通知します。 ブックマーク到達イベントをトリガーするには、SSMLbookmark 要素が必要です。 このイベントは、合成の開始から bookmark 要素までの間の出力オーディオの経過時間を報告します。 イベントの Text プロパティは、ブックマークの mark 属性で設定した文字列値です。 bookmark 要素は読み上げられません。 bookmark 要素を使用すると、SSML にカスタム マーカーを挿入して、オーディオ ストリーム内の各マーカーのオフセットを取得できます。 bookmark 要素は、テキストまたはタグのシーケンス内の特定の場所を参照するために使用できます。
SynthesisCanceled 音声合成が取り消されたことを通知します。 合成が取り消されたときに、それを確認できます。
SynthesisCompleted 音声合成が完了したことを通知します。 合成が完了したときに、それを確認できます。
SynthesisStarted 音声合成が開始したことを通知します。 合成が開始したときに、それを確認できます。
Synthesizing 音声合成が進行中であることを通知します。 このイベントは、SDK が Speech サービスからオーディオ チャンクを受信するたびに発生します。 合成が進行中のときに、それを確認できます。
VisemeReceived 口形素イベントが受信されたことを通知します。 観察された発話における主要な姿勢を表すために、口形素がよく使用されます。 主要な姿勢には、特定の音素を生成するときの唇、顎、舌の位置などが含まれます。 口形素を使用して、スピーチ音声の再生に伴って、キャラクターに顔のアニメーションを付けることができます。
WordBoundary 単語境界が受信されたことを通知します。 このイベントは、新しい音声、句読点、文それぞれの先頭で発生します。 イベントによって、出力音声の開始から現在の単語までのタイム オフセット (ティック単位) が報告されます。 このイベントは、読み上げられようとしている単語の直前に、入力テキスト (または SSML) の文字位置も報告します。 このイベントは、一般にテキストおよび対応するオーディオの相対位置を取得するために使用されます。 新しい単語について知り、タイミングに基づいてアクションを実行できます。 たとえば、単語を読み上げ時に強調表示するタイミングと時間を決定するのに役立つ情報を取得できます。

Note

イベントは、出力オーディオ データが使用可能になると発生します。これは、出力デバイスへの再生よりも高速です。 呼び出し元は、ストリーミングと実際の時間を適切に同期する必要があります。

音声合成のためにイベントをサブスクライブする方法を示す例を次に示します。 クイックスタートの手順に従うことができますが、その SpeechSynthesis.java ファイルの内容を次の Java コードに置き換えます:

import com.microsoft.cognitiveservices.speech.*;
import com.microsoft.cognitiveservices.speech.audio.*;

import java.util.Scanner;
import java.util.concurrent.ExecutionException;

public class SpeechSynthesis {
    // This example requires environment variables named "SPEECH_KEY" and "SPEECH_REGION"
    private static String speechKey = System.getenv("SPEECH_KEY");
    private static String speechRegion = System.getenv("SPEECH_REGION");

    public static void main(String[] args) throws InterruptedException, ExecutionException {

        SpeechConfig speechConfig = SpeechConfig.fromSubscription(speechKey, speechRegion);
        
        // Required for WordBoundary event sentences.
        speechConfig.setProperty(PropertyId.SpeechServiceResponse_RequestSentenceBoundary, "true");

        String speechSynthesisVoiceName = "en-US-AvaMultilingualNeural"; 
        
        String ssml = String.format("<speak version='1.0' xml:lang='en-US' xmlns='http://www.w3.org/2001/10/synthesis' xmlns:mstts='http://www.w3.org/2001/mstts'>"
            .concat(String.format("<voice name='%s'>", speechSynthesisVoiceName))
            .concat("<mstts:viseme type='redlips_front'/>")
            .concat("The rainbow has seven colors: <bookmark mark='colors_list_begin'/>Red, orange, yellow, green, blue, indigo, and violet.<bookmark mark='colors_list_end'/>.")
            .concat("</voice>")
            .concat("</speak>"));

        SpeechSynthesizer speechSynthesizer = new SpeechSynthesizer(speechConfig);
        {
            // Subscribe to events

            speechSynthesizer.BookmarkReached.addEventListener((o, e) -> {
                System.out.println("BookmarkReached event:");
                System.out.println("\tAudioOffset: " + ((e.getAudioOffset() + 5000) / 10000) + "ms");
                System.out.println("\tText: " + e.getText());
            });

            speechSynthesizer.SynthesisCanceled.addEventListener((o, e) -> {
                System.out.println("SynthesisCanceled event");
            });

            speechSynthesizer.SynthesisCompleted.addEventListener((o, e) -> {
                SpeechSynthesisResult result = e.getResult();                
                byte[] audioData = result.getAudioData();
                System.out.println("SynthesisCompleted event:");
                System.out.println("\tAudioData: " + audioData.length + " bytes");
                System.out.println("\tAudioDuration: " + result.getAudioDuration());
                result.close();
            });
            
            speechSynthesizer.SynthesisStarted.addEventListener((o, e) -> {
                System.out.println("SynthesisStarted event");
            });

            speechSynthesizer.Synthesizing.addEventListener((o, e) -> {
                SpeechSynthesisResult result = e.getResult();
                byte[] audioData = result.getAudioData();
                System.out.println("Synthesizing event:");
                System.out.println("\tAudioData: " + audioData.length + " bytes");
                result.close();
            });

            speechSynthesizer.VisemeReceived.addEventListener((o, e) -> {
                System.out.println("VisemeReceived event:");
                System.out.println("\tAudioOffset: " + ((e.getAudioOffset() + 5000) / 10000) + "ms");
                System.out.println("\tVisemeId: " + e.getVisemeId());
            });

            speechSynthesizer.WordBoundary.addEventListener((o, e) -> {
                System.out.println("WordBoundary event:");
                System.out.println("\tBoundaryType: " + e.getBoundaryType());
                System.out.println("\tAudioOffset: " + ((e.getAudioOffset() + 5000) / 10000) + "ms");
                System.out.println("\tDuration: " + e.getDuration());
                System.out.println("\tText: " + e.getText());
                System.out.println("\tTextOffset: " + e.getTextOffset());
                System.out.println("\tWordLength: " + e.getWordLength());
            });

            // Synthesize the SSML
            System.out.println("SSML to synthesize:");
            System.out.println(ssml);
            SpeechSynthesisResult speechSynthesisResult = speechSynthesizer.SpeakSsmlAsync(ssml).get();

            if (speechSynthesisResult.getReason() == ResultReason.SynthesizingAudioCompleted) {
                System.out.println("SynthesizingAudioCompleted result");
            }
            else if (speechSynthesisResult.getReason() == ResultReason.Canceled) {
                SpeechSynthesisCancellationDetails cancellation = SpeechSynthesisCancellationDetails.fromResult(speechSynthesisResult);
                System.out.println("CANCELED: Reason=" + cancellation.getReason());

                if (cancellation.getReason() == CancellationReason.Error) {
                    System.out.println("CANCELED: ErrorCode=" + cancellation.getErrorCode());
                    System.out.println("CANCELED: ErrorDetails=" + cancellation.getErrorDetails());
                    System.out.println("CANCELED: Did you set the speech resource key and region values?");
                }
            }
        }
        speechSynthesizer.close();

        System.exit(0);
    }
}

その他のテキスト読み上げサンプルは GitHub にあります。

カスタム エンドポイントを使用する

カスタム エンドポイントの機能は、テキスト読み上げ要求に使用される標準のエンドポイントと同じです。

1 つ違うのは、Speech SDK でカスタム音声を使うには、EndpointId を指定する必要があることです。 テキスト読み上げのクイックスタートから始めて、EndpointIdSpeechSynthesisVoiceName でコードを更新できます。

SpeechConfig speechConfig = SpeechConfig.fromSubscription(speechKey, speechRegion);
speechConfig.setSpeechSynthesisVoiceName("YourCustomVoiceName");
speechConfig.setEndpointId("YourEndpointId");

音声合成マークアップ言語 (SSML) でカスタム音声を使うには、音声名としてモデル名を指定します。 この例では、YourCustomVoiceName 音声を使用します。

<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US">
    <voice name="YourCustomVoiceName">
        This is the text that is spoken. 
    </voice>
</speak>

コンテナーを実行して使用する

音声コンテナーは、WebSocket ベースのクエリ エンドポイント API シリーズを提供します。これには、Speech SDK および Speech CLI を介してアクセスします。 既定では、Speech SDK と Speech CLI ではパブリック音声サービスが使用されます。 コンテナーを使用するには、初期化方法を変更する必要があります。 キーとリージョンではなく、コンテナー ホスト URL を使用します。

音声コンテナーの詳細については、「Docker を使用して Speech コンテナーをインストールして実行する」を参照してください。

リファレンスドキュメント | パッケージ (npm) | GitHub のその他のサンプル | ライブラリのソース コード

この攻略ガイドでは、テキスト読み上げ合成を行うための一般的な設計パターンについて説明します。

次の領域の詳細については、「テキスト読み上げとは」を参照してください。

  • インメモリ ストリームとしての応答の取得。
  • 出力のサンプル レートとビット レートのカスタマイズ。
  • 音声合成マークアップ言語 (SSML) を使用した合成要求の送信。
  • ニューラル音声の使用。
  • イベントのサブスクライブと、結果に対するアクションの実行。

合成言語と音声を選択する

Speech サービスのテキスト読み上げ機能では、400 を超える音声と、140 を超える言語およびバリアントがサポートされています。 すべてのリストを入手することも、音声ギャラリーでそれらを試すこともできます。

入力テキストに合わせて SpeechConfig の言語または音声を指定し、必要な音声を使用します:

function synthesizeSpeech() {
    const speechConfig = sdk.SpeechConfig.fromSubscription("YourSpeechKey", "YourSpeechRegion");
    // Set either the `SpeechSynthesisVoiceName` or `SpeechSynthesisLanguage`.
    speechConfig.speechSynthesisLanguage = "en-US"; 
    speechConfig.speechSynthesisVoiceName = "en-US-AvaMultilingualNeural";
}

synthesizeSpeech();

すべてのニューラル音声は多言語で、独自の言語と英語で流暢に読み上げます。 たとえば、英語の入力テキストが "I'm excited to try text to speech" で、es-ES-ElviraNeural を設定した場合、テキストはスペイン語アクセントの英語で読み上げられます。

入力テキストの言語が音声で読み上げられない場合、Speech サービスでは合成された音声が作成されません。 サポートされているニューラル音声の完全な一覧については、「Azure Cognitive Service for Speech サービスの言語と音声のサポート」を参照してください。

Note

既定の音声は、Voice List API からロケールに従って返される最初の音声です。

読み上げられる音声は、次の優先順序で決定されます。

  • SpeechSynthesisVoiceName または SpeechSynthesisLanguage を設定していない場合、en-US の既定の音声が読み上げられます。
  • SpeechSynthesisLanguage のみを設定した場合、指定したロケールの既定の音声が読み上げられます。
  • SpeechSynthesisVoiceNameSpeechSynthesisLanguage の両方を設定した場合、SpeechSynthesisLanguage の設定は無視されます。 SpeechSynthesisVoiceName を使用して指定した音声が読み上げられます。
  • 音声要素を音声合成マークアップ言語 (SSML) で設定した場合、SpeechSynthesisVoiceNameSpeechSynthesisLanguage の設定は無視されます。

テキスト読み上げを合成する

合成された音声をスピーカーなどの現在アクティブな出力デバイスに出力するには、fromDefaultSpeakerOutput() 静的関数を使用して AudioConfig をインスタンス化します。 次に例を示します。

function synthesizeSpeech() {
    const speechConfig = sdk.SpeechConfig.fromSubscription("YourSpeechKey", "YourSpeechRegion");
    const audioConfig = sdk.AudioConfig.fromDefaultSpeakerOutput();

    const speechSynthesizer = new SpeechSynthesizer(speechConfig, audioConfig);
    speechSynthesizer.speakTextAsync(
        "I'm excited to try text to speech",
        result => {
            if (result) {
                speechSynthesizer.close();
                return result.audioData;
            }
        },
        error => {
            console.log(error);
            speechSynthesizer.close();
        });
}

プログラムを実行すると、合成されたオーディオがスピーカーから再生されます。 この結果は、最も基本的な使用方法の好例です。 次に、カスタム シナリオに対応できるように、出力をカスタマイズし、出力応答をインメモリ ストリームとして処理することができます。

結果をインメモリ ストリームとして取得する

結果として得られたオーディオ データは、ファイルに直接書き込むのではなく、インメモリ ストリームとして使用できます。 インメモリ ストリームを使用すると、カスタム動作を構築できます:

  • 結果として得られるバイト配列を、カスタム ダウンストリーム サービス向けのシーク可能なストリームとして抽象化します。
  • 結果を他の API やサービスと統合します。
  • オーディオ データの変更、カスタム .wav ヘッダーの記述、関連するタスクの実行を行います。

前の例にこの変更を加えることができます。 AudioConfig ブロックを削除します。これは、この時点から先は出力動作を手動で管理して制御を強化するためです。 次に、SpeechSynthesizer コンストラクターの AudioConfignull を渡します。

Note

前出のスピーカー出力の例で行ったように省略するのではなく、AudioConfignull を渡すと、既定では、現在のアクティブな出力デバイスではオーディオが再生されません。

結果を SpeechSynthesisResult 変数に保存します。 SpeechSynthesisResult.audioData プロパティでは、既定のブラウザー ストリーム型である、出力データの ArrayBuffer 値が返されます。 サーバー側コードの場合は、ArrayBuffer をバッファー ストリームに変換します。

次のコードは、クライアント側に対して機能します。

function synthesizeSpeech() {
    const speechConfig = sdk.SpeechConfig.fromSubscription("YourSpeechKey", "YourSpeechRegion");
    const speechSynthesizer = new sdk.SpeechSynthesizer(speechConfig);

    speechSynthesizer.speakTextAsync(
        "I'm excited to try text to speech",
        result => {
            speechSynthesizer.close();
            return result.audioData;
        },
        error => {
            console.log(error);
            speechSynthesizer.close();
        });
}

ここから、結果として得られた ArrayBuffer オブジェクトを使用して、任意のカスタム動作を実装できます。 ArrayBuffer は、ブラウザーで受信し、この形式から再生する共通型です。

サーバー ベースのコードでは、ストリームとしてデータを操作する必要がある場合は、ArrayBuffer オブジェクトをストリームに変換する必要があります。

function synthesizeSpeech() {
    const speechConfig = sdk.SpeechConfig.fromSubscription("YourSpeechKey", "YourSpeechRegion");
    const speechSynthesizer = new sdk.SpeechSynthesizer(speechConfig);

    speechSynthesizer.speakTextAsync(
        "I'm excited to try text to speech",
        result => {
            const { audioData } = result;

            speechSynthesizer.close();

            // convert arrayBuffer to stream
            // return stream
            const bufferStream = new PassThrough();
            bufferStream.end(Buffer.from(audioData));
            return bufferStream;
        },
        error => {
            console.log(error);
            speechSynthesizer.close();
        });
}

オーディオ形式をカスタマイズする

以下を含むオーディオ出力属性をカスタマイズできます。

  • オーディオ ファイルの種類
  • サンプル速度
  • ビット深度

オーディオ形式を変更するには、SpeechConfig オブジェクトで speechSynthesisOutputFormat プロパティを使用します。 このプロパティでは、SpeechSynthesisOutputFormat 型の enum インスタンスが必要です。 enum を使用して出力形式を選択します。 使用可能な形式については、「オーディオ形式の一覧」を参照してください。

要件に応じて、異なるファイルの種類に向けたさまざまなオプションがあります。 定義上、Raw24Khz16BitMonoPcm のような未加工の形式には、オーディオ ヘッダーが含まれていません。 未加工の形式は、次のいずれかの状況でのみ使用してください。

  • ダウンストリームの実装で未加工のビットストリームをデコードできることがわかっている。
  • ビット深度、サンプル レート、チャネル数のような要因に基づいて、ヘッダーを手動で構築する予定である。

この例では、SpeechConfig オブジェクトに speechSynthesisOutputFormat を設定して、忠実度が高い RIFF 形式 Riff24Khz16BitMonoPcm を指定しています。 前のセクションの例と同様に、オーディオ ArrayBuffer データを取得して操作します。

function synthesizeSpeech() {
    const speechConfig = SpeechConfig.fromSubscription("YourSpeechKey", "YourSpeechRegion");

    // Set the output format
    speechConfig.speechSynthesisOutputFormat = sdk.SpeechSynthesisOutputFormat.Riff24Khz16BitMonoPcm;

    const speechSynthesizer = new sdk.SpeechSynthesizer(speechConfig, null);
    speechSynthesizer.speakTextAsync(
        "I'm excited to try text to speech",
        result => {
            // Interact with the audio ArrayBuffer data
            const audioData = result.audioData;
            console.log(`Audio data byte size: ${audioData.byteLength}.`)

            speechSynthesizer.close();
        },
        error => {
            console.log(error);
            speechSynthesizer.close();
        });
}

SSML を使用して音声の特徴をカスタマイズする

SSML を使用すると、XML スキーマから要求を送信して、テキスト読み上げ出力でのピッチ、発音、読み上げ速度、ボリューム、その他の面を微調整できます。 このセクションでは、音声を変更する例を示します。 詳しくは、「音声合成マークアップ言語の概要」をご覧ください。

SSML を使用したカスタマイズを開始するには、音声を切り替える小規模変更を加えます。

  1. ルート プロジェクト ディレクトリに SSML 構成用の新しい XML ファイルを作成します。

    <speak version="1.0" xmlns="https://www.w3.org/2001/10/synthesis" xml:lang="en-US">
      <voice name="en-US-AvaMultilingualNeural">
        When you're on the freeway, it's a good idea to use a GPS.
      </voice>
    </speak>
    

    この例では、ssml.xml です。 ルート要素は常に <speak> です。 テキストを <voice> 要素にラップすると、name パラメーターを使用して音声を変更できます。 サポートされているニューラル音声の一覧については、「サポートされている言語」を参照してください。

  2. XML ファイルを参照するように音声合成要求を変更する必要があります。 要求はほとんど同じですが、speakTextAsync() 関数を使用する代わりに、speakSsmlAsync() を使用します。 この関数は XML 文字列を必要とします。 XML ファイルを読み込んでそれを文字列として返す関数を作成します:

    function xmlToString(filePath) {
        const xml = readFileSync(filePath, "utf8");
        return xml;
    }
    

    readFileSync の詳細については、「Node.js ファイル システム」を参照してください。

    ここからは、結果のオブジェクトは前の例とまったく同じです:

    function synthesizeSpeech() {
        const speechConfig = sdk.SpeechConfig.fromSubscription("YourSpeechKey", "YourSpeechRegion");
        const speechSynthesizer = new sdk.SpeechSynthesizer(speechConfig, null);
    
        const ssml = xmlToString("ssml.xml");
        speechSynthesizer.speakSsmlAsync(
            ssml,
            result => {
                if (result.errorDetails) {
                    console.error(result.errorDetails);
                } else {
                    console.log(JSON.stringify(result));
                }
    
                speechSynthesizer.close();
            },
            error => {
                console.log(error);
                speechSynthesizer.close();
            });
    }
    

Note

SSML を使用せずに音声を変更するには、SpeechConfig.speechSynthesisVoiceName = "en-US-AvaMultilingualNeural"; を使用して SpeechConfig のプロパティを設定します。

シンセサイザー イベントをサブスクライブする

テキスト読み上げ処理と結果に関する、より多くの分析情報が必要になる場合があります。 たとえば、シンセサイザーが開始および停止したときにそれを知りたい場合や、合成中に発生した他のイベントについて知りたい場合があります。

テキスト読み上げに SpeechSynthesizer を使用している間は、次の表のイベントをサブスクライブできます。

Event 説明 使用事例
BookmarkReached ブックマークに到達したことを通知します。 ブックマーク到達イベントをトリガーするには、SSMLbookmark 要素が必要です。 このイベントは、合成の開始から bookmark 要素までの間の出力オーディオの経過時間を報告します。 イベントの Text プロパティは、ブックマークの mark 属性で設定した文字列値です。 bookmark 要素は読み上げられません。 bookmark 要素を使用すると、SSML にカスタム マーカーを挿入して、オーディオ ストリーム内の各マーカーのオフセットを取得できます。 bookmark 要素は、テキストまたはタグのシーケンス内の特定の場所を参照するために使用できます。
SynthesisCanceled 音声合成が取り消されたことを通知します。 合成が取り消されたときに、それを確認できます。
SynthesisCompleted 音声合成が完了したことを通知します。 合成が完了したときに、それを確認できます。
SynthesisStarted 音声合成が開始したことを通知します。 合成が開始したときに、それを確認できます。
Synthesizing 音声合成が進行中であることを通知します。 このイベントは、SDK が Speech サービスからオーディオ チャンクを受信するたびに発生します。 合成が進行中のときに、それを確認できます。
VisemeReceived 口形素イベントが受信されたことを通知します。 観察された発話における主要な姿勢を表すために、口形素がよく使用されます。 主要な姿勢には、特定の音素を生成するときの唇、顎、舌の位置などが含まれます。 口形素を使用して、スピーチ音声の再生に伴って、キャラクターに顔のアニメーションを付けることができます。
WordBoundary 単語境界が受信されたことを通知します。 このイベントは、新しい音声、句読点、文それぞれの先頭で発生します。 イベントによって、出力音声の開始から現在の単語までのタイム オフセット (ティック単位) が報告されます。 このイベントは、読み上げられようとしている単語の直前に、入力テキスト (または SSML) の文字位置も報告します。 このイベントは、一般にテキストおよび対応するオーディオの相対位置を取得するために使用されます。 新しい単語について知り、タイミングに基づいてアクションを実行できます。 たとえば、単語を読み上げ時に強調表示するタイミングと時間を決定するのに役立つ情報を取得できます。

Note

イベントは、出力オーディオ データが使用可能になると発生します。これは、出力デバイスへの再生よりも高速です。 呼び出し元は、ストリーミングと実際の時間を適切に同期する必要があります。

音声合成のためにイベントをサブスクライブする方法を示す例を次に示します。 クイックスタートの手順に従うことができますが、その SppechSynthesis.js ファイルの内容を次の JavaScript コードに置き換えます。

(function() {

    "use strict";

    var sdk = require("microsoft-cognitiveservices-speech-sdk");

    var audioFile = "YourAudioFile.wav";
    // This example requires environment variables named "SPEECH_KEY" and "SPEECH_REGION"
    const speechConfig = sdk.SpeechConfig.fromSubscription(process.env.SPEECH_KEY, process.env.SPEECH_REGION);
    const audioConfig = sdk.AudioConfig.fromAudioFileOutput(audioFile);

    var speechSynthesisVoiceName  = "en-US-AvaMultilingualNeural";  
    var ssml = `<speak version='1.0' xml:lang='en-US' xmlns='http://www.w3.org/2001/10/synthesis' xmlns:mstts='http://www.w3.org/2001/mstts'> \r\n \
        <voice name='${speechSynthesisVoiceName}'> \r\n \
            <mstts:viseme type='redlips_front'/> \r\n \
            The rainbow has seven colors: <bookmark mark='colors_list_begin'/>Red, orange, yellow, green, blue, indigo, and violet.<bookmark mark='colors_list_end'/>. \r\n \
        </voice> \r\n \
    </speak>`;
    
    // Required for WordBoundary event sentences.
    speechConfig.setProperty(sdk.PropertyId.SpeechServiceResponse_RequestSentenceBoundary, "true");

    // Create the speech speechSynthesizer.
    var speechSynthesizer = new sdk.SpeechSynthesizer(speechConfig, audioConfig);

    speechSynthesizer.bookmarkReached = function (s, e) {
        var str = `BookmarkReached event: \
            \r\n\tAudioOffset: ${(e.audioOffset + 5000) / 10000}ms \
            \r\n\tText: \"${e.text}\".`;
        console.log(str);
    };

    speechSynthesizer.synthesisCanceled = function (s, e) {
        console.log("SynthesisCanceled event");
    };
    
    speechSynthesizer.synthesisCompleted = function (s, e) {
        var str = `SynthesisCompleted event: \
                    \r\n\tAudioData: ${e.result.audioData.byteLength} bytes \
                    \r\n\tAudioDuration: ${e.result.audioDuration}`;
        console.log(str);
    };

    speechSynthesizer.synthesisStarted = function (s, e) {
        console.log("SynthesisStarted event");
    };

    speechSynthesizer.synthesizing = function (s, e) {
        var str = `Synthesizing event: \
            \r\n\tAudioData: ${e.result.audioData.byteLength} bytes`;
        console.log(str);
    };
    
    speechSynthesizer.visemeReceived = function(s, e) {
        var str = `VisemeReceived event: \
            \r\n\tAudioOffset: ${(e.audioOffset + 5000) / 10000}ms \
            \r\n\tVisemeId: ${e.visemeId}`;
        console.log(str);
    };

    speechSynthesizer.wordBoundary = function (s, e) {
        // Word, Punctuation, or Sentence
        var str = `WordBoundary event: \
            \r\n\tBoundaryType: ${e.boundaryType} \
            \r\n\tAudioOffset: ${(e.audioOffset + 5000) / 10000}ms \
            \r\n\tDuration: ${e.duration} \
            \r\n\tText: \"${e.text}\" \
            \r\n\tTextOffset: ${e.textOffset} \
            \r\n\tWordLength: ${e.wordLength}`;
        console.log(str);
    };

    // Synthesize the SSML
    console.log(`SSML to synthesize: \r\n ${ssml}`)
    console.log(`Synthesize to: ${audioFile}`);
    speechSynthesizer.speakSsmlAsync(ssml,
        function (result) {
      if (result.reason === sdk.ResultReason.SynthesizingAudioCompleted) {
        console.log("SynthesizingAudioCompleted result");
      } else {
        console.error("Speech synthesis canceled, " + result.errorDetails +
            "\nDid you set the speech resource key and region values?");
      }
      speechSynthesizer.close();
      speechSynthesizer = null;
    },
        function (err) {
      console.trace("err - " + err);
      speechSynthesizer.close();
      speechSynthesizer = null;
    });
}());

その他のテキスト読み上げサンプルは GitHub にあります。

コンテナーを実行して使用する

音声コンテナーは、WebSocket ベースのクエリ エンドポイント API シリーズを提供します。これには、Speech SDK および Speech CLI を介してアクセスします。 既定では、Speech SDK と Speech CLI ではパブリック音声サービスが使用されます。 コンテナーを使用するには、初期化方法を変更する必要があります。 キーとリージョンではなく、コンテナー ホスト URL を使用します。

音声コンテナーの詳細については、「Docker を使用して Speech コンテナーをインストールして実行する」を参照してください。

リファレンス ドキュメント | パッケージ (ダウンロード) | GitHub のその他のサンプル

この攻略ガイドでは、テキスト読み上げ合成を行うための一般的な設計パターンについて説明します。

次の領域の詳細については、「テキスト読み上げとは」を参照してください。

  • インメモリ ストリームとしての応答の取得。
  • 出力のサンプル レートとビット レートのカスタマイズ。
  • 音声合成マークアップ言語 (SSML) を使用した合成要求の送信。
  • ニューラル音声の使用。
  • イベントのサブスクライブと、結果に対するアクションの実行。

前提条件

Speech SDK とサンプルをインストールする

Azure-Samples/cognitive-services-speech-sdk リポジトリには、iOS および Mac 向けに Objective-C で記述されたサンプルが含まれています。 リンクを選択して、各サンプルのインストール手順を参照してください。

カスタム エンドポイントを使用する

カスタム エンドポイントの機能は、テキスト読み上げ要求に使用される標準のエンドポイントと同じです。

1 つ違うのは、Speech SDK でカスタム音声を使うには、EndpointId を指定する必要があることです。 テキスト読み上げのクイックスタートから始めて、EndpointIdSpeechSynthesisVoiceName でコードを更新できます。

SPXSpeechConfiguration *speechConfig = [[SPXSpeechConfiguration alloc] initWithSubscription:speechKey region:speechRegion];
speechConfig.speechSynthesisVoiceName = @"YourCustomVoiceName";
speechConfig.EndpointId = @"YourEndpointId";

音声合成マークアップ言語 (SSML) でカスタム音声を使うには、音声名としてモデル名を指定します。 この例では、YourCustomVoiceName 音声を使用します。

<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US">
    <voice name="YourCustomVoiceName">
        This is the text that is spoken. 
    </voice>
</speak>

コンテナーを実行して使用する

音声コンテナーは、WebSocket ベースのクエリ エンドポイント API シリーズを提供します。これには、Speech SDK および Speech CLI を介してアクセスします。 既定では、Speech SDK と Speech CLI ではパブリック音声サービスが使用されます。 コンテナーを使用するには、初期化方法を変更する必要があります。 キーとリージョンではなく、コンテナー ホスト URL を使用します。

音声コンテナーの詳細については、「Docker を使用して Speech コンテナーをインストールして実行する」を参照してください。

リファレンス ドキュメント | パッケージ (ダウンロード) | GitHub のその他のサンプル

この攻略ガイドでは、テキスト読み上げ合成を行うための一般的な設計パターンについて説明します。

次の領域の詳細については、「テキスト読み上げとは」を参照してください。

  • インメモリ ストリームとしての応答の取得。
  • 出力のサンプル レートとビット レートのカスタマイズ。
  • 音声合成マークアップ言語 (SSML) を使用した合成要求の送信。
  • ニューラル音声の使用。
  • イベントのサブスクライブと、結果に対するアクションの実行。

前提条件

Speech SDK とサンプルをインストールする

Azure-Samples/cognitive-services-speech-sdk リポジトリには、iOS および Mac 向けに Swift で記述されたサンプルが含まれています。 リンクを選択して、各サンプルのインストール手順を参照してください。

コンテナーを実行して使用する

音声コンテナーは、WebSocket ベースのクエリ エンドポイント API シリーズを提供します。これには、Speech SDK および Speech CLI を介してアクセスします。 既定では、Speech SDK と Speech CLI ではパブリック音声サービスが使用されます。 コンテナーを使用するには、初期化方法を変更する必要があります。 キーとリージョンではなく、コンテナー ホスト URL を使用します。

音声コンテナーの詳細については、「Docker を使用して Speech コンテナーをインストールして実行する」を参照してください。

リファレンス ドキュメント | パッケージ (PyPi) | GitHub のその他のサンプル

この攻略ガイドでは、テキスト読み上げ合成を行うための一般的な設計パターンについて説明します。

次の領域の詳細については、「テキスト読み上げとは」を参照してください。

  • インメモリ ストリームとしての応答の取得。
  • 出力のサンプル レートとビット レートのカスタマイズ。
  • 音声合成マークアップ言語 (SSML) を使用した合成要求の送信。
  • ニューラル音声の使用。
  • イベントのサブスクライブと、結果に対するアクションの実行。

合成言語と音声を選択する

Speech サービスのテキスト読み上げ機能では、400 を超える音声と、140 を超える言語およびバリアントがサポートされています。 すべてのリストを入手することも、音声ギャラリーでそれらを試すこともできます。

入力テキストに合わせて SpeechConfig の言語または音声を指定し、必要な音声を使用します:

# Set either the `SpeechSynthesisVoiceName` or `SpeechSynthesisLanguage`.
speech_config.speech_synthesis_language = "en-US" 
speech_config.speech_synthesis_voice_name ="en-US-AvaMultilingualNeural"

すべてのニューラル音声は多言語で、独自の言語と英語で流暢に読み上げます。 たとえば、英語の入力テキストが "I'm excited to try text to speech" で、es-ES-ElviraNeural を設定した場合、テキストはスペイン語アクセントの英語で読み上げられます。

入力テキストの言語が音声で読み上げられない場合、Speech サービスでは合成された音声が作成されません。 サポートされているニューラル音声の完全な一覧については、「Azure Cognitive Service for Speech サービスの言語と音声のサポート」を参照してください。

Note

既定の音声は、Voice List API からロケールに従って返される最初の音声です。

読み上げられる音声は、次の優先順序で決定されます。

  • SpeechSynthesisVoiceName または SpeechSynthesisLanguage を設定していない場合、en-US の既定の音声が読み上げられます。
  • SpeechSynthesisLanguage のみを設定した場合、指定したロケールの既定の音声が読み上げられます。
  • SpeechSynthesisVoiceNameSpeechSynthesisLanguage の両方を設定した場合、SpeechSynthesisLanguage の設定は無視されます。 SpeechSynthesisVoiceName を使用して指定した音声が読み上げられます。
  • 音声要素を音声合成マークアップ言語 (SSML) で設定した場合、SpeechSynthesisVoiceNameSpeechSynthesisLanguage の設定は無視されます。

音声をファイルに合成する

SpeechSynthesizer オブジェクトを作成します。 このオブジェクトにより、テキスト読み上げ変換と、スピーカー、ファイル、またはその他の出力ストリームへの出力が実行されます。 SpeechSynthesizer は、パラメーターとして以下を受け取ります。

  • 前の手順で作成した SpeechConfig オブジェクト
  • 出力結果をどのように処理する必要があるかを指定している AudioOutputConfig オブジェクト。
  1. filename コンストラクター パラメーターを使用して .wav ファイルに出力を自動的に書き込む AudioOutputConfig インスタンスを作成します。

    audio_config = speechsdk.audio.AudioOutputConfig(filename="path/to/write/file.wav")
    
  2. speech_config オブジェクトと audio_config オブジェクトをパラメーターとして渡して SpeechSynthesizer をインスタンス化します。 音声を合成してファイルに書き込むには、文字列で speak_text_async() を実行します。

    speech_synthesizer = speechsdk.SpeechSynthesizer(speech_config=speech_config, audio_config=audio_config)
    speech_synthesizer.speak_text_async("I'm excited to try text to speech")
    

プログラムを実行すると、合成された .wav ファイルが作成され、指定した場所に書き込まれます。 この結果は、最も基本的な使用方法の好例です。 次に、カスタム シナリオに対応できるように、出力をカスタマイズし、出力応答をインメモリ ストリームとして処理することができます。

スピーカー出力に合成する

合成された音声をスピーカーなどの現在アクティブな出力デバイスに出力するには、AudioOutputConfig インスタンスの作成時に use_default_speaker パラメーターを設定します。 次に例を示します。

audio_config = speechsdk.audio.AudioOutputConfig(use_default_speaker=True)

結果をインメモリ ストリームとして取得する

結果として得られたオーディオ データは、ファイルに直接書き込むのではなく、インメモリ ストリームとして使用できます。 インメモリ ストリームを使用すると、カスタム動作を構築できます:

  • 結果として得られるバイト配列を、カスタム ダウンストリーム サービス向けのシーク可能なストリームとして抽象化します。
  • 結果を他の API やサービスと統合します。
  • オーディオ データの変更、カスタム .wav ヘッダーの記述、関連するタスクの実行を行います。

前の例にこの変更を加えることができます。 まず、AudioConfig を削除します。これは、この時点から先は出力動作を手動で管理して制御を強化するためです。 SpeechSynthesizer コンストラクターの AudioConfigNone を渡します。

Note

前出のスピーカー出力の例で行ったように省略するのではなく、AudioConfigNone を渡すと、既定では、現在のアクティブな出力デバイスではオーディオが再生されなくなります。

結果を SpeechSynthesisResult 変数に保存します。 audio_data プロパティには、出力データの bytes オブジェクトが含まれます。 このオブジェクトを手動で操作することも、AudioDataStream クラスを使用してインメモリ ストリームを管理することもできます。

この例では、AudioDataStream コンストラクターを使用して、結果からストリームを取得します。

speech_synthesizer = speechsdk.SpeechSynthesizer(speech_config=speech_config, audio_config=None)
result = speech_synthesizer.speak_text_async("I'm excited to try text to speech").get()
stream = AudioDataStream(result)

この時点で、結果として得られた stream オブジェクトを使用して、任意のカスタム動作を実装できます。

オーディオ形式をカスタマイズする

以下を含むオーディオ出力属性をカスタマイズできます。

  • オーディオ ファイルの種類
  • サンプル速度
  • ビット深度

オーディオ形式を変更するには、SpeechConfig オブジェクトで set_speech_synthesis_output_format() 関数を使用します。 この関数では、SpeechSynthesisOutputFormat 型の enum インスタンスが必要です。 enum を使用して出力形式を選択します。 使用可能な形式については、「オーディオ形式の一覧」を参照してください。

要件に応じて、異なるファイルの種類に向けたさまざまなオプションがあります。 定義上、Raw24Khz16BitMonoPcm のような未加工の形式には、オーディオ ヘッダーが含まれていません。 未加工の形式は、次のいずれかの状況でのみ使用してください。

  • ダウンストリームの実装で未加工のビットストリームをデコードできることがわかっている。
  • ビット深度、サンプル レート、チャネル数のような要因に基づいて、ヘッダーを手動で構築する予定である。

この例では、SpeechConfig オブジェクトに SpeechSynthesisOutputFormat を設定して、忠実度が高い RIFF 形式 Riff24Khz16BitMonoPcm を指定しています。 前のセクションの例と同様に、AudioDataStream を使用して結果のインメモリ ストリームを取得し、それをファイルに書き込みます。

speech_config.set_speech_synthesis_output_format(speechsdk.SpeechSynthesisOutputFormat.Riff24Khz16BitMonoPcm)
speech_synthesizer = speechsdk.SpeechSynthesizer(speech_config=speech_config, audio_config=None)

result = speech_synthesizer.speak_text_async("I'm excited to try text to speech").get()
stream = speechsdk.AudioDataStream(result)
stream.save_to_wav_file("path/to/write/file.wav")

プログラムを実行すると、指定したパスに .wav ファイルが書き込まれます。

SSML を使用して音声の特徴をカスタマイズする

SSML を使用すると、XML スキーマから要求を送信して、テキスト読み上げ出力でのピッチ、発音、読み上げ速度、ボリューム、その他の面を微調整できます。 このセクションでは、音声を変更する例を示します。 詳しくは、「音声合成マークアップ言語の概要」をご覧ください。

SSML を使用したカスタマイズを開始するには、音声を切り替える小規模変更を加えます。

  1. ルート プロジェクト ディレクトリに SSML 構成用の新しい XML ファイルを作成します。

    <speak version="1.0" xmlns="https://www.w3.org/2001/10/synthesis" xml:lang="en-US">
      <voice name="en-US-AvaMultilingualNeural">
        When you're on the freeway, it's a good idea to use a GPS.
      </voice>
    </speak>
    

    この例では、ファイルは ssml.xmlです。 ルート要素は常に <speak> です。 テキストを <voice> 要素にラップすると、name パラメーターを使用して音声を変更できます。 サポートされているニューラル音声の一覧については、「サポートされている言語」を参照してください。

  2. XML ファイルを参照するように音声合成要求を変更する必要があります。 要求はほぼ同じです。 speak_text_async() 関数を使用する代わりに、speak_ssml_async() を使用します。 この関数は XML 文字列を必要とします。 まず、SSML 構成を文字列として読み取ります。 この時点からは、結果のオブジェクトは前の例とまったく同じです。

    Note

    ssml_string が文字列の先頭に  を含んでいる場合は、BOM 形式を削除する必要があります。そうしないと、サービスからエラーが返されます。 これを行うには、encoding パラメーターを次のように設定します: open("ssml.xml", "r", encoding="utf-8-sig")

    speech_synthesizer = speechsdk.SpeechSynthesizer(speech_config=speech_config, audio_config=None)
    
    ssml_string = open("ssml.xml", "r").read()
    result = speech_synthesizer.speak_ssml_async(ssml_string).get()
    
    stream = speechsdk.AudioDataStream(result)
    stream.save_to_wav_file("path/to/write/file.wav")
    

注意

SSML を使用せずに音声を変更するには、speech_config.speech_synthesis_voice_name = "en-US-AvaMultilingualNeural" を使用して SpeechConfig のプロパティを設定します。

シンセサイザー イベントをサブスクライブする

テキスト読み上げ処理と結果に関する、より多くの分析情報が必要になる場合があります。 たとえば、シンセサイザーが開始および停止したときにそれを知りたい場合や、合成中に発生した他のイベントについて知りたい場合があります。

テキスト読み上げに SpeechSynthesizer を使用している間は、次の表のイベントをサブスクライブできます。

Event 説明 使用事例
BookmarkReached ブックマークに到達したことを通知します。 ブックマーク到達イベントをトリガーするには、SSMLbookmark 要素が必要です。 このイベントは、合成の開始から bookmark 要素までの間の出力オーディオの経過時間を報告します。 イベントの Text プロパティは、ブックマークの mark 属性で設定した文字列値です。 bookmark 要素は読み上げられません。 bookmark 要素を使用すると、SSML にカスタム マーカーを挿入して、オーディオ ストリーム内の各マーカーのオフセットを取得できます。 bookmark 要素は、テキストまたはタグのシーケンス内の特定の場所を参照するために使用できます。
SynthesisCanceled 音声合成が取り消されたことを通知します。 合成が取り消されたときに、それを確認できます。
SynthesisCompleted 音声合成が完了したことを通知します。 合成が完了したときに、それを確認できます。
SynthesisStarted 音声合成が開始したことを通知します。 合成が開始したときに、それを確認できます。
Synthesizing 音声合成が進行中であることを通知します。 このイベントは、SDK が Speech サービスからオーディオ チャンクを受信するたびに発生します。 合成が進行中のときに、それを確認できます。
VisemeReceived 口形素イベントが受信されたことを通知します。 観察された発話における主要な姿勢を表すために、口形素がよく使用されます。 主要な姿勢には、特定の音素を生成するときの唇、顎、舌の位置などが含まれます。 口形素を使用して、スピーチ音声の再生に伴って、キャラクターに顔のアニメーションを付けることができます。
WordBoundary 単語境界が受信されたことを通知します。 このイベントは、新しい音声、句読点、文それぞれの先頭で発生します。 イベントによって、出力音声の開始から現在の単語までのタイム オフセット (ティック単位) が報告されます。 このイベントは、読み上げられようとしている単語の直前に、入力テキスト (または SSML) の文字位置も報告します。 このイベントは、一般にテキストおよび対応するオーディオの相対位置を取得するために使用されます。 新しい単語について知り、タイミングに基づいてアクションを実行できます。 たとえば、単語を読み上げ時に強調表示するタイミングと時間を決定するのに役立つ情報を取得できます。

Note

イベントは、出力オーディオ データが使用可能になると発生します。これは、出力デバイスへの再生よりも高速です。 呼び出し元は、ストリーミングと実際の時間を適切に同期する必要があります。

音声合成のためにイベントをサブスクライブする方法を示す例を次に示します。 クイックスタートの手順に従うことができますが、その sppech-synthesis.py ファイルの内容を次の Python コードに置き換えます:

import os
import azure.cognitiveservices.speech as speechsdk

def speech_synthesizer_bookmark_reached_cb(evt: speechsdk.SessionEventArgs):
    print('BookmarkReached event:')
    print('\tAudioOffset: {}ms'.format((evt.audio_offset + 5000) / 10000))
    print('\tText: {}'.format(evt.text))

def speech_synthesizer_synthesis_canceled_cb(evt: speechsdk.SessionEventArgs):
    print('SynthesisCanceled event')

def speech_synthesizer_synthesis_completed_cb(evt: speechsdk.SessionEventArgs):
    print('SynthesisCompleted event:')
    print('\tAudioData: {} bytes'.format(len(evt.result.audio_data)))
    print('\tAudioDuration: {}'.format(evt.result.audio_duration))

def speech_synthesizer_synthesis_started_cb(evt: speechsdk.SessionEventArgs):
    print('SynthesisStarted event')

def speech_synthesizer_synthesizing_cb(evt: speechsdk.SessionEventArgs):
    print('Synthesizing event:')
    print('\tAudioData: {} bytes'.format(len(evt.result.audio_data)))

def speech_synthesizer_viseme_received_cb(evt: speechsdk.SessionEventArgs):
    print('VisemeReceived event:')
    print('\tAudioOffset: {}ms'.format((evt.audio_offset + 5000) / 10000))
    print('\tVisemeId: {}'.format(evt.viseme_id))

def speech_synthesizer_word_boundary_cb(evt: speechsdk.SessionEventArgs):
    print('WordBoundary event:')
    print('\tBoundaryType: {}'.format(evt.boundary_type))
    print('\tAudioOffset: {}ms'.format((evt.audio_offset + 5000) / 10000))
    print('\tDuration: {}'.format(evt.duration))
    print('\tText: {}'.format(evt.text))
    print('\tTextOffset: {}'.format(evt.text_offset))
    print('\tWordLength: {}'.format(evt.word_length))

# This example requires environment variables named "SPEECH_KEY" and "SPEECH_REGION"
speech_config = speechsdk.SpeechConfig(subscription=os.environ.get('SPEECH_KEY'), region=os.environ.get('SPEECH_REGION'))

# Required for WordBoundary event sentences.
speech_config.set_property(property_id=speechsdk.PropertyId.SpeechServiceResponse_RequestSentenceBoundary, value='true')

audio_config = speechsdk.audio.AudioOutputConfig(use_default_speaker=True)
speech_synthesizer = speechsdk.SpeechSynthesizer(speech_config=speech_config, audio_config=audio_config)

# Subscribe to events
speech_synthesizer.bookmark_reached.connect(speech_synthesizer_bookmark_reached_cb)
speech_synthesizer.synthesis_canceled.connect(speech_synthesizer_synthesis_canceled_cb)
speech_synthesizer.synthesis_completed.connect(speech_synthesizer_synthesis_completed_cb)
speech_synthesizer.synthesis_started.connect(speech_synthesizer_synthesis_started_cb)
speech_synthesizer.synthesizing.connect(speech_synthesizer_synthesizing_cb)
speech_synthesizer.viseme_received.connect(speech_synthesizer_viseme_received_cb)
speech_synthesizer.synthesis_word_boundary.connect(speech_synthesizer_word_boundary_cb)

# The language of the voice that speaks.
speech_synthesis_voice_name='en-US-AvaMultilingualNeural'

ssml = """<speak version='1.0' xml:lang='en-US' xmlns='http://www.w3.org/2001/10/synthesis' xmlns:mstts='http://www.w3.org/2001/mstts'>
    <voice name='{}'>
        <mstts:viseme type='redlips_front'/>
        The rainbow has seven colors: <bookmark mark='colors_list_begin'/>Red, orange, yellow, green, blue, indigo, and violet.<bookmark mark='colors_list_end'/>.
    </voice>
</speak>""".format(speech_synthesis_voice_name)

# Synthesize the SSML
print("SSML to synthesize: \r\n{}".format(ssml))
speech_synthesis_result = speech_synthesizer.speak_ssml_async(ssml).get()

if speech_synthesis_result.reason == speechsdk.ResultReason.SynthesizingAudioCompleted:
    print("SynthesizingAudioCompleted result")
elif speech_synthesis_result.reason == speechsdk.ResultReason.Canceled:
    cancellation_details = speech_synthesis_result.cancellation_details
    print("Speech synthesis canceled: {}".format(cancellation_details.reason))
    if cancellation_details.reason == speechsdk.CancellationReason.Error:
        if cancellation_details.error_details:
            print("Error details: {}".format(cancellation_details.error_details))
            print("Did you set the speech resource key and region values?")

その他のテキスト読み上げサンプルは GitHub にあります。

カスタム エンドポイントを使用する

カスタム エンドポイントの機能は、テキスト読み上げ要求に使用される標準のエンドポイントと同じです。

1 つ違うのは、Speech SDK でカスタム音声を使うには、endpoint_id を指定する必要があることです。 テキスト読み上げのクイックスタートから始めて、endpoint_idspeech_synthesis_voice_name でコードを更新できます。

speech_config = speechsdk.SpeechConfig(subscription=os.environ.get('SPEECH_KEY'), region=os.environ.get('SPEECH_REGION'))
speech_config.endpoint_id = "YourEndpointId"
speech_config.speech_synthesis_voice_name = "YourCustomVoiceName"

音声合成マークアップ言語 (SSML) でカスタム音声を使うには、音声名としてモデル名を指定します。 この例では、YourCustomVoiceName 音声を使用します。

<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US">
    <voice name="YourCustomVoiceName">
        This is the text that is spoken. 
    </voice>
</speak>

コンテナーを実行して使用する

音声コンテナーは、WebSocket ベースのクエリ エンドポイント API シリーズを提供します。これには、Speech SDK および Speech CLI を介してアクセスします。 既定では、Speech SDK と Speech CLI ではパブリック音声サービスが使用されます。 コンテナーを使用するには、初期化方法を変更する必要があります。 キーとリージョンではなく、コンテナー ホスト URL を使用します。

音声コンテナーの詳細については、「Docker を使用して Speech コンテナーをインストールして実行する」を参照してください。

Speech to text REST API リファレンス | Speech to text REST API for short audio リファレンス | GitHub のその他のサンプル

この攻略ガイドでは、テキスト読み上げ合成を行うための一般的な設計パターンについて説明します。

次の領域の詳細については、「テキスト読み上げとは」を参照してください。

  • インメモリ ストリームとしての応答の取得。
  • 出力のサンプル レートとビット レートのカスタマイズ。
  • 音声合成マークアップ言語 (SSML) を使用した合成要求の送信。
  • ニューラル音声の使用。
  • イベントのサブスクライブと、結果に対するアクションの実行。

前提条件

テキストを音声に変換する

コマンド プロンプトで、次のコマンドを実行します。 コマンドには以下の値を挿入します。

  • Speech リソースのキー
  • Speech リソース リージョン

以下の値を変更することもできます。

  • オーディオ出力形式を制御する X-Microsoft-OutputFormat ヘッダー値。 サポートされているオーディオ出力形式の一覧については、「REST API リファレンス」を参照してください。
  • 出力音声。 お使いの音声サービス エンドポイントで使用できる音声の一覧を取得するには、Voice List API に関するページを参照してください。
  • 出力ファイル。 この例では、サーバーからの応答を output.mp3 という名前のファイルに送信します。
curl --location --request POST 'https://YOUR_RESOURCE_REGION.tts.speech.microsoft.com/cognitiveservices/v1' \
--header 'Ocp-Apim-Subscription-Key: YOUR_RESOURCE_KEY' \
--header 'Content-Type: application/ssml+xml' \
--header 'X-Microsoft-OutputFormat: audio-16khz-128kbitrate-mono-mp3' \
--header 'User-Agent: curl' \
--data-raw '<speak version='\''1.0'\'' xml:lang='\''en-US'\''>
    <voice name='\''en-US-AvaMultilingualNeural'\''>
        I am excited to try text to speech
    </voice>
</speak>' > output.mp3

この攻略ガイドでは、テキスト読み上げ合成を行うための一般的な設計パターンについて説明します。

次の領域の詳細については、「テキスト読み上げとは」を参照してください。

  • インメモリ ストリームとしての応答の取得。
  • 出力のサンプル レートとビット レートのカスタマイズ。
  • 音声合成マークアップ言語 (SSML) を使用した合成要求の送信。
  • ニューラル音声の使用。
  • イベントのサブスクライブと、結果に対するアクションの実行。

前提条件

Management Studio のダウンロードとインストールは、

次の手順を実行し、対象プラットフォームに対する他の要件を Azure Cognitive Service for Speech CLI クイックスタートで確認します。

  1. 次の .NET CLI コマンドを実行して、Speech CLI をインストールします。

    dotnet tool install --global Microsoft.CognitiveServices.Speech.CLI
    
  2. 次のコマンドを実行して、Azure Cognitive Service for Speech リソースのキーとリージョンを構成します。 SUBSCRIPTION-KEY は Speech リソースのキーに、REGION は Speech リソースのリージョンに置き換えます。

    spx config @key --set SUBSCRIPTION-KEY
    spx config @region --set REGION
    

スピーカーに音声を合成する

Speech CLI を実行して、音声をテキストに合成する準備ができました。

  • コンソール ウィンドウで、Speech CLI バイナリ ファイルを含むディレクトリに変更します。 次に、次のコマンドを実行します。

    spx synthesize --text "I'm excited to try text to speech"
    

Speech CLI では、コンピューターのスピーカーを通じて、英語の自然言語を生成します。

音声をファイルに合成する

  • 次のコマンドを実行して、出力をスピーカーから .wav ファイルに変更します。

    spx synthesize --text "I'm excited to try text to speech" --audio output greetings.wav
    

Speech CLI により、greetings.wav オーディオ ファイル内に英語の自然言語が生成されます。

コンテナーを実行して使用する

音声コンテナーは、WebSocket ベースのクエリ エンドポイント API シリーズを提供します。これには、Speech SDK および Speech CLI を介してアクセスします。 既定では、Speech SDK と Speech CLI ではパブリック音声サービスが使用されます。 コンテナーを使用するには、初期化方法を変更する必要があります。 キーとリージョンではなく、コンテナー ホスト URL を使用します。

音声コンテナーの詳細については、「Docker を使用して Speech コンテナーをインストールして実行する」を参照してください。

次のステップ