クイック スタート: 話しているユーザーを認識して確認する

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

このクイックスタートでは、Speech SDK を使用して、話者認識の基本的な設計パターンを学習します。これには、次のものが含まれます。

  • テキスト依存およびテキスト非依存の認証。
  • 音声のグループから音声サンプルを識別する話者識別。
  • 音声プロファイルの削除。

話者認識の概念の概要については、概要に関するページを参照してください。 サポートされているプラットフォームの一覧については、左側のナビゲーションの参照ノードを参照してください。

重要

Microsoft では、話者認識へのアクセスを制限しています。 話者認識を使用するには、Azure AI Speaker Recognition 制限付きアクセス レビュー フォームを使用して申し込んでください。 承認後、Speaker Recognition API にアクセスできます。

前提条件

Speech SDK のインストール

開始する前に、Speech SDK をインストールする必要があります。 ご利用のプラットフォームに応じて、次の手順を行います。

依存関係のインポート

この記事の例を実行するには、スクリプトの先頭に以下の using ステートメントを含めます。

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.CognitiveServices.Speech;
using Microsoft.CognitiveServices.Speech.Audio;

音声構成を作成する

Speech SDK を使用して音声サービスを呼び出すには、SpeechConfig インスタンスを作成する必要があります。 この例では、サブスクリプション キーとリージョンを使用して SpeechConfig インスタンスを作成します。 また、この記事の残りの部分で使用する、基本的な定型コードをいくつか作成します。これを変更して、さまざまなカスタマイズを行います。

重要

終わったらコードからキーを削除し、公開しないよう注意してください。 運用環境では、Azure Key Vault などの資格情報を格納してアクセスする安全な方法を使用します。 詳細については、Azure AI サービスのセキュリティに関する記事を参照してください。

public class Program 
{
    static async Task Main(string[] args)
    {
        // replace with your own subscription key 
        string subscriptionKey = "YourSubscriptionKey";
        // replace with your own subscription region 
        string region = "YourSubscriptionRegion";
        var config = SpeechConfig.FromSubscription(subscriptionKey, region);
    }
}

テキスト依存の認証

話者認証では、話者の音声が、既知または登録済みの音声と一致することを確認します。 最初の手順は、音声プロファイルの登録です。これは、サービスで、今後の音声サンプルとの比較に使用されます。 この例では、テキスト依存の戦略を使用してプロファイルを登録します。これには、登録と認証に使用する特定のパスフレーズが必要です。 サポートされるパスフレーズの一覧については、リファレンス ドキュメントを参照してください。

まず、次の関数を Program クラスに作成して音声プロファイルを登録します。

public static async Task VerificationEnroll(SpeechConfig config, Dictionary<string, string> profileMapping)
{
    using (var client = new VoiceProfileClient(config))
    using (var profile = await client.CreateProfileAsync(VoiceProfileType.TextDependentVerification, "en-us"))
    {
        var phraseResult = await client.GetActivationPhrasesAsync(VoiceProfileType.TextDependentVerification, "en-us");
        using (var audioInput = AudioConfig.FromDefaultMicrophoneInput())
        {
            Console.WriteLine($"Enrolling profile id {profile.Id}.");
            // give the profile a human-readable display name
            profileMapping.Add(profile.Id, "Your Name");

            VoiceProfileEnrollmentResult result = null;
            while (result is null || result.RemainingEnrollmentsCount > 0)
            {
                Console.WriteLine($"Speak the passphrase, \"${phraseResult.Phrases[0]}\"");
                result = await client.EnrollProfileAsync(profile, audioInput);
                Console.WriteLine($"Remaining enrollments needed: {result.RemainingEnrollmentsCount}");
                Console.WriteLine("");
            }
            
            if (result.Reason == ResultReason.EnrolledVoiceProfile)
            {
                await SpeakerVerify(config, profile, profileMapping);
            }
            else if (result.Reason == ResultReason.Canceled)
            {
                var cancellation = VoiceProfileEnrollmentCancellationDetails.FromResult(result);
                Console.WriteLine($"CANCELED {profile.Id}: ErrorCode={cancellation.ErrorCode} ErrorDetails={cancellation.ErrorDetails}");
            }
        }
    }
}

この関数では、await client.CreateProfileAsync() で実際に新しい音声プロファイルが作成されます。 プロファイルが作成されたら、オーディオ サンプルを入力する方法を指定します。この例では AudioConfig.FromDefaultMicrophoneInput() を使用して、既定の入力デバイスからオーディオをキャプチャします。 次に、while ループにオーディオ サンプルを登録します。このループで、登録する残りのサンプルと、登録に必要なサンプルの数が追跡されます。 繰り返されるたびに、client.EnrollProfileAsync(profile, audioInput) によって、マイクに向かってパスフレーズを読み上げることが求められ、サンプルが音声プロファイルに追加されます。

登録が完了したら、await SpeakerVerify(config, profile, profileMapping) を呼び出して、作成したプロファイルに対して認証を行います。 SpeakerVerify を定義する別の関数を追加します。

public static async Task SpeakerVerify(SpeechConfig config, VoiceProfile profile, Dictionary<string, string> profileMapping)
{
    var speakerRecognizer = new SpeakerRecognizer(config, AudioConfig.FromDefaultMicrophoneInput());
    var model = SpeakerVerificationModel.FromProfile(profile);

    Console.WriteLine("Speak the passphrase to verify: \"My voice is my passport, please verify me.\"");
    var result = await speakerRecognizer.RecognizeOnceAsync(model);
    Console.WriteLine($"Verified voice profile for speaker {profileMapping[result.ProfileId]}, score is {result.Score}");
}

この関数では、先ほど作成した VoiceProfile オブジェクトを渡して、認証対象のモデルを初期化します。 次に、await speakerRecognizer.RecognizeOnceAsync(model) によって、パスフレーズの読み上げを求めるメッセージが再び表示されます。 今回は、音声プロファイルに対して検証が行われ、0.0 ~ 1.0 の範囲の類似性スコアが返されます。 result オブジェクトにより、パスフレーズが一致するかどうかに基づいて、Accept または Reject も返されます。

次に、Main() 関数を変更し、作成した新しい関数を呼び出します。 また、Dictionary<string, string> を作成して、関数呼び出しによる参照渡しも行います。 これは、サービスでは、作成された VoiceProfile と共に、人間が判読できる名前を格納することが許可されず、プライバシー上の目的で ID 番号のみが格納されるためです。 VerificationEnroll 関数では、このディクショナリに、テキスト名と共に、新しく作成された ID を使用してエントリを追加します。 人間が判読できる名前を表示する必要のあるアプリケーション開発シナリオでは、サービスがこのマッピングを格納できないために、どこかに格納する必要があります

static async Task Main(string[] args)
{
    string subscriptionKey = "YourSubscriptionKey";
    string region = "westus";
    var config = SpeechConfig.FromSubscription(subscriptionKey, region);

    // persist profileMapping if you want to store a record of who the profile is
    var profileMapping = new Dictionary<string, string>();
    await VerificationEnroll(config, profileMapping);

    Console.ReadLine();
}

スクリプトを実行します。 "My voice is my passport, verify me" というフレーズを、登録のために 3 回、さらに認証のために 1 回、読み上げるように求められます。 その結果、類似性スコアが返されます。これを使用して、認証用に独自のカスタムしきい値を作成することができます。

Enrolling profile id 87-2cef-4dff-995b-dcefb64e203f.
Speak the passphrase, "My voice is my passport, verify me."
Remaining enrollments needed: 2

Speak the passphrase, "My voice is my passport, verify me."
Remaining enrollments needed: 1

Speak the passphrase, "My voice is my passport, verify me."
Remaining enrollments needed: 0

Speak the passphrase to verify: "My voice is my passport, verify me."
Verified voice profile for speaker Your Name, score is 0.915581

テキストに依存しない認証

テキスト依存検証とは対照的に、テキスト非依存検証では、3 つのオーディオ サンプルは必要ありませんが、合計 20 秒のオーディオが必要です。

VerificationEnroll 関数にいくつかの単純な変更を加え、テキストに依存しない認証に切り替えます。 まず、認証の種類を VoiceProfileType.TextIndependentVerification に変更します。 次に、result.RemainingEnrollmentsSpeechLength を追跡するために while ループを変更します。その場合、20 秒のオーディオがキャプチャされるまで引き続き話しかけるように求められます。

public static async Task VerificationEnroll(SpeechConfig config, Dictionary<string, string> profileMapping)
{
    using (var client = new VoiceProfileClient(config))
    using (var profile = await client.CreateProfileAsync(VoiceProfileType.TextIndependentVerification, "en-us"))
    {
        var phraseResult = await client.GetActivationPhrasesAsync(VoiceProfileType.TextIndependentVerification, "en-us");
        using (var audioInput = AudioConfig.FromDefaultMicrophoneInput())
        {
            Console.WriteLine($"Enrolling profile id {profile.Id}.");
            // give the profile a human-readable display name
            profileMapping.Add(profile.Id, "Your Name");

            VoiceProfileEnrollmentResult result = null;
            while (result is null || result.RemainingEnrollmentsSpeechLength > TimeSpan.Zero)
            {
                Console.WriteLine($"Speak the activation phrase, \"${phraseResult.Phrases[0]}\"");
                result = await client.EnrollProfileAsync(profile, audioInput);
                Console.WriteLine($"Remaining enrollment audio time needed: {result.RemainingEnrollmentsSpeechLength}");
                Console.WriteLine("");
            }
            
            if (result.Reason == ResultReason.EnrolledVoiceProfile)
            {
                await SpeakerVerify(config, profile, profileMapping);
            }
            else if (result.Reason == ResultReason.Canceled)
            {
                var cancellation = VoiceProfileEnrollmentCancellationDetails.FromResult(result);
                Console.WriteLine($"CANCELED {profile.Id}: ErrorCode={cancellation.ErrorCode} ErrorDetails={cancellation.ErrorDetails}");
            }
        }
    }
}

プログラムを再度実行すると、類似性スコアが返されます。

Enrolling profile id 4tt87d4-f2d3-44ae-b5b4-f1a8d4036ee9.
Speak the activation phrase, "<FIRST ACTIVATION PHRASE>"
Remaining enrollment audio time needed: 00:00:15.3200000

Speak the activation phrase, "<FIRST ACTIVATION PHRASE>"
Remaining enrollment audio time needed: 00:00:09.8100008

Speak the activation phrase, "<FIRST ACTIVATION PHRASE>"
Remaining enrollment audio time needed: 00:00:05.1900000

Speak the activation phrase, "<FIRST ACTIVATION PHRASE>"
Remaining enrollment audio time needed: 00:00:00.8700000

Speak the activation phrase, "<FIRST ACTIVATION PHRASE>"
Remaining enrollment audio time needed: 00:00:00

Speak the passphrase to verify: "My voice is my passport, please verify me."
Verified voice profile for speaker Your Name, score is 0.849409

話者識別

話者識別を使用して、登録されている音声の特定のグループのが話しているのかを判別します。 このプロセスは、テキスト非依存検証に似ています。 主な違いは、1 つのプロファイルに対して検証するのではなく、複数の音声プロファイルに対して同時に検証する機能です。

複数の音声プロファイルを登録するための関数 IdentificationEnroll を作成します。 各プロファイルの登録プロセスは、テキスト非依存検証の登録プロセスと同じです。 このプロセスには、プロファイルごとに 20 秒のオーディオが必要です。 この関数では、profileNames という文字列のリストが受理され、リストに含まれる名前ごとに、新しい音声プロファイルが作成されます。 関数からは VoiceProfile オブジェクトのリストが返されます。これは、次の関数で話者を識別するために使用します。

public static async Task<List<VoiceProfile>> IdentificationEnroll(SpeechConfig config, List<string> profileNames, Dictionary<string, string> profileMapping)
{
    List<VoiceProfile> voiceProfiles = new List<VoiceProfile>();
    using (var client = new VoiceProfileClient(config))
    {
        var phraseResult = await client.GetActivationPhrasesAsync(VoiceProfileType.TextIndependentVerification, "en-us");
        foreach (string name in profileNames)
        {
            using (var audioInput = AudioConfig.FromDefaultMicrophoneInput())
            {
                var profile = await client.CreateProfileAsync(VoiceProfileType.TextIndependentIdentification, "en-us");
                Console.WriteLine($"Creating voice profile for {name}.");
                profileMapping.Add(profile.Id, name);

                VoiceProfileEnrollmentResult result = null;
                while (result is null || result.RemainingEnrollmentsSpeechLength > TimeSpan.Zero)
                {
                    Console.WriteLine($"Speak the activation phrase, \"${phraseResult.Phrases[0]}\" to add to the profile enrollment sample for {name}.");
                    result = await client.EnrollProfileAsync(profile, audioInput);
                    Console.WriteLine($"Remaining enrollment audio time needed: {result.RemainingEnrollmentsSpeechLength}");
                    Console.WriteLine("");
                }
                voiceProfiles.Add(profile);
            }
        }
    }
    return voiceProfiles;
}

次の関数 SpeakerIdentification を作成して、識別要求を送信します。 話者認証要求と比較した場合のこの関数の主な違いは、VoiceProfile オブジェクトのリストを受け入れる、SpeakerIdentificationModel.FromProfiles() を使用することです。

public static async Task SpeakerIdentification(SpeechConfig config, List<VoiceProfile> voiceProfiles, Dictionary<string, string> profileMapping) 
{
    var speakerRecognizer = new SpeakerRecognizer(config, AudioConfig.FromDefaultMicrophoneInput());
    var model = SpeakerIdentificationModel.FromProfiles(voiceProfiles);

    Console.WriteLine("Speak some text to identify who it is from your list of enrolled speakers.");
    var result = await speakerRecognizer.RecognizeOnceAsync(model);
    Console.WriteLine($"The most similar voice profile is {profileMapping[result.ProfileId]} with similarity score {result.Score}");
}

Main() 関数を次のように変更します。 IdentificationEnroll() 関数に渡す、profileNames という文字列のリストを作成します。 このリストに含まれる名前ごとに、新しい音声プロファイルを作成するように求められます。こうすると、さらに名前を追加して、友人や同僚のプロファイルを作成することができます。

static async Task Main(string[] args)
{
    // replace with your own subscription key 
    string subscriptionKey = "YourSubscriptionKey";
    // replace with your own subscription region 
    string region = "YourSubscriptionRegion";
    var config = SpeechConfig.FromSubscription(subscriptionKey, region);

    // persist profileMapping if you want to store a record of who the profile is
    var profileMapping = new Dictionary<string, string>();
    var profileNames = new List<string>() { "Your name", "A friend's name" };
    
    var enrolledProfiles = await IdentificationEnroll(config, profileNames, profileMapping);
    await SpeakerIdentification(config, enrolledProfiles, profileMapping);

    foreach (var profile in enrolledProfiles)
    {
        profile.Dispose();
    }
    Console.ReadLine();
}

スクリプトを実行します。 最初のプロファイル用の音声サンプルを登録するために話しかけるように求められます。 登録が終了したら、profileNames リストに含まれる名前ごとに、このプロセスを繰り返すように求められます。 それぞれの登録が終了したら、誰かが話しかけるように求めるメッセージが表示 されます。 その後、サービスは、登録された音声プロファイルの中から、この人物を識別しようとします。

この例では、最も近い一致とその類似性スコアのみが返されます。 上位 5 つの類似性スコアを含む応答全体を取得するには、SpeakerIdentification 関数に string json = result.Properties.GetProperty(PropertyId.SpeechServiceResponse_JsonResult) を追加します。

オーディオ入力の種類を変更する

この記事の例では、オーディオ サンプルの入力として既定のデバイス マイクを使用しています。 マイク入力の代わりにオーディオ ファイルを使用する必要のあるシナリオでは、AudioConfig.FromDefaultMicrophoneInput() のすべてのインスタンスを AudioConfig.FromWavFileInput(path/to/your/file.wav) に変更してファイル入力に切り替えます。 登録用にはマイクを、認証用にはファイルを使用して、入力の種類を使い分けることもできます。

音声プロファイルの登録を削除する

登録されているプロファイルを削除するには、VoiceProfileClient オブジェクトで DeleteProfileAsync() 関数を使用します。 次の関数の例は、既知の音声プロファイル ID から音声プロファイルを削除する方法を示します。

public static async Task DeleteProfile(SpeechConfig config, string profileId) 
{
    using (var client = new VoiceProfileClient(config))
    {
        var profile = new VoiceProfile(profileId);
        await client.DeleteProfileAsync(profile);
    }
}

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

このクイックスタートでは、Speech SDK を使用して、話者認識の基本的な設計パターンを学習します。これには、次のものが含まれます。

  • テキスト依存およびテキスト非依存の認証。
  • 音声のグループから音声サンプルを識別する話者識別。
  • 音声プロファイルの削除。

話者認識の概念の概要については、概要に関するページを参照してください。 サポートされているプラットフォームの一覧については、左側のナビゲーションの参照ノードを参照してください。

重要

Microsoft では、話者認識へのアクセスを制限しています。 話者認識を使用するには、Azure AI Speaker Recognition 制限付きアクセス レビュー フォームを使用して申し込んでください。 承認後、Speaker Recognition API にアクセスできます。

前提条件

Speech SDK のインストール

開始する前に、Speech SDK をインストールする必要があります。 ご利用のプラットフォームに応じて、次の手順を行います。

依存関係のインポート

この記事の例を実行するには、.cpp ファイルの先頭に次のステートメントを追加します。

#include <iostream>
#include <stdexcept>
// Note: Install the NuGet package Microsoft.CognitiveServices.Speech.
#include <speechapi_cxx.h>

using namespace std;
using namespace Microsoft::CognitiveServices::Speech;

// Note: Change the locale if desired.
auto profile_locale = "en-us";
auto audio_config = Audio::AudioConfig::FromDefaultMicrophoneInput();
auto ticks_per_second = 10000000;

音声構成を作成する

Speech SDK を使用して音声サービスを呼び出すには、SpeechConfig クラスを作成します。 このクラスには、キー、関連付けられたリージョン、エンドポイント、ホスト、または認証トークンなど、ご利用のサブスクリプションに関する情報が含まれています。

重要

終わったらコードからキーを削除し、公開しないよう注意してください。 運用環境では、Azure Key Vault などの資格情報を格納してアクセスする安全な方法を使用します。 詳細については、Azure AI サービスのセキュリティに関する記事を参照してください。

shared_ptr<SpeechConfig> GetSpeechConfig()
{
    auto subscription_key = 'PASTE_YOUR_SPEECH_SUBSCRIPTION_KEY_HERE';
    auto region = 'PASTE_YOUR_SPEECH_ENDPOINT_REGION_HERE';
    auto config = SpeechConfig::FromSubscription(subscription_key, region);
    return config;
}

テキスト依存の認証

話者認証では、話者の音声が、既知または登録済みの音声と一致することを確認します。 最初の手順は、音声プロファイルの登録です。これは、サービスで、今後の音声サンプルとの比較に使用されます。 この例では、テキスト依存の戦略を使用してプロファイルを登録します。これには、登録と認証に使用する特定のパスフレーズが必要です。 サポートされるパスフレーズの一覧については、リファレンス ドキュメントを参照してください。

TextDependentVerification 関数

最初に TextDependentVerification 関数を作成します。

void TextDependentVerification(shared_ptr<VoiceProfileClient> client, shared_ptr<SpeakerRecognizer> recognizer)
{
    std::cout << "Text Dependent Verification:\n\n";
    // Create the profile.
    auto profile = client->CreateProfileAsync(VoiceProfileType::TextDependentVerification, profile_locale).get();
    std::cout << "Created profile ID: " << profile->GetId() << "\n";
    AddEnrollmentsToTextDependentProfile(client, profile);
    SpeakerVerify(profile, recognizer);
    // Delete the profile.
    client->DeleteProfileAsync(profile);
}

この関数では、CreateProfileAsync メソッドを使用して VoiceProfile オブジェクトを作成します。 次の 3 種類VoiceProfile があります。

  • TextIndependentIdentification
  • TextDependentVerification
  • TextIndependentVerification

この場合は、VoiceProfileType::TextDependentVerificationCreateProfileAsync に渡します。

その後、次に定義する 2 つのヘルパー関数 AddEnrollmentsToTextDependentProfileSpeakerVerify を呼び出します。 最後に、DeleteProfileAsync を呼び出してプロファイルをクリーンアップします。

AddEnrollmentsToTextDependentProfile 関数

次の関数を定義して音声プロファイルを登録します。

void AddEnrollmentsToTextDependentProfile(shared_ptr<VoiceProfileClient> client, shared_ptr<VoiceProfile> profile)
{
    shared_ptr<VoiceProfileEnrollmentResult> enroll_result = nullptr;
    auto phraseResult = client->GetActivationPhrasesAsync(profile->GetType(), profile_locale).get();
    auto phrases = phraseResult->GetPhrases();
    while (enroll_result == nullptr || enroll_result->GetEnrollmentInfo(EnrollmentInfoType::RemainingEnrollmentsCount) > 0)
    {
        if (phrases != nullptr && phrases->size() > 0)
        {
            std::cout << "Please say the passphrase, \"" << phrases->at(0) << "\"\n";
            enroll_result = client->EnrollProfileAsync(profile, audio_config).get();
            std::cout << "Remaining enrollments needed: " << enroll_result->GetEnrollmentInfo(EnrollmentInfoType::RemainingEnrollmentsCount) << ".\n";
        }
        else
        {
            std::cout << "No passphrases received, enrollment not attempted.\n\n";
        }
    }
    std::cout << "Enrollment completed.\n\n";
}

この関数では、while ループにオーディオ サンプルを登録します。このループで、登録する残りのサンプルと、登録に必要なサンプルの数が追跡されます。 繰り返されるたびに、EnrollProfileAsync によって、マイクに向かってパスフレーズを読み上げるように求められ、サンプルが音声プロファイルに追加されます。

SpeakerVerify 関数

次のように SpeakerVerify を定義します。

void SpeakerVerify(shared_ptr<VoiceProfile> profile, shared_ptr<SpeakerRecognizer> recognizer)
{
    shared_ptr<SpeakerVerificationModel> model = SpeakerVerificationModel::FromProfile(profile);
    std::cout << "Speak the passphrase to verify: \"My voice is my passport, verify me.\"\n";
    shared_ptr<SpeakerRecognitionResult> result = recognizer->RecognizeOnceAsync(model).get();
    std::cout << "Verified voice profile for speaker: " << result->ProfileId << ". Score is: " << result->GetScore() << ".\n\n";
}

この関数では、SpeakerVerificationModel::FromProfile メソッドを使用して SpeakerVerificationModel オブジェクトを作成し、先ほど作成した VoiceProfile オブジェクトを渡します。

次に、SpeechRecognizer::RecognizeOnceAsync によって、もう一度パスフレーズを読み上げるよう求められます。 今回は、音声プロファイルに対して検証が行われ、0.0 ~ 1.0 の範囲の類似性スコアが返されます。 また、SpeakerRecognitionResult オブジェクトによって、パスフレーズが一致するかどうかに基づいて、Accept または Reject も返されます。

テキストに依存しない認証

テキスト依存検証とは対照的に、テキスト非依存検証では、3 つのオーディオ サンプルは必要ありませんが、合計 20 秒のオーディオが必要です。

TextIndependentVerification 関数

最初に TextIndependentVerification 関数を作成します。

void TextIndependentVerification(shared_ptr<VoiceProfileClient> client, shared_ptr<SpeakerRecognizer> recognizer)
{
    std::cout << "Text Independent Verification:\n\n";
    // Create the profile.
    auto profile = client->CreateProfileAsync(VoiceProfileType::TextIndependentVerification, profile_locale).get();
    std::cout << "Created profile ID: " << profile->GetId() << "\n";
    AddEnrollmentsToTextIndependentProfile(client, profile);
    SpeakerVerify(profile, recognizer);
    // Delete the profile.
    client->DeleteProfileAsync(profile);
}

TextDependentVerification 関数と同様に、この関数では、CreateProfileAsync メソッドを使用して VoiceProfile オブジェクトを作成します。

この場合は、VoiceProfileType::TextIndependentVerificationCreateProfileAsync に渡します。

その後、2 つのヘルパー関数を呼び出します。1 つは次に定義する AddEnrollmentsToTextIndependentProfile、もう 1 つは既に定義した SpeakerVerify です。 最後に、DeleteProfileAsync を呼び出してプロファイルをクリーンアップします。

AddEnrollmentsToTextIndependentProfile

次の関数を定義して音声プロファイルを登録します。

void AddEnrollmentsToTextIndependentProfile(shared_ptr<VoiceProfileClient> client, shared_ptr<VoiceProfile> profile)
{
    shared_ptr<VoiceProfileEnrollmentResult> enroll_result = nullptr;
    auto phraseResult = client->GetActivationPhrasesAsync(profile->GetType(), profile_locale).get();
    auto phrases = phraseResult->GetPhrases();
    while (enroll_result == nullptr || enroll_result->GetEnrollmentInfo(EnrollmentInfoType::RemainingEnrollmentsSpeechLength) > 0)
    {
        if (phrases != nullptr && phrases->size() > 0)
        {
            std::cout << "Please say the activation phrase, \"" << phrases->at(0) << "\"\n";
            enroll_result = client->EnrollProfileAsync(profile, audio_config).get();
            std::cout << "Remaining audio time needed: " << enroll_result->GetEnrollmentInfo(EnrollmentInfoType::RemainingEnrollmentsSpeechLength) / ticks_per_second << " seconds.\n";
        }
        else
        {
            std::cout << "No activation phrases received, enrollment not attempted.\n\n";
        }
    }
    std::cout << "Enrollment completed.\n\n";
}

この関数では、while ループにオーディオ サンプルが登録されます。このループで、登録する残りのオーディオと、登録に必要なオーディオの秒数が追跡されます。 繰り返されるたびに、EnrollProfileAsync によって、マイクに向かって話すように求められ、サンプルが音声プロファイルに追加されます。

話者識別

話者識別を使用して、登録されている音声の特定のグループのが話しているのかを判別します。 このプロセスは、テキスト非依存検証に似ています。 主な違いは、1 つのプロファイルに対して検証するのではなく、複数の音声プロファイルに対して同時に検証する機能です。

TextIndependentIdentification 関数

最初に TextIndependentIdentification 関数を作成します。

void TextIndependentIdentification(shared_ptr<VoiceProfileClient> client, shared_ptr<SpeakerRecognizer> recognizer)
{
    std::cout << "Speaker Identification:\n\n";
    // Create the profile.
    auto profile = client->CreateProfileAsync(VoiceProfileType::TextIndependentIdentification, profile_locale).get();
    std::cout << "Created profile ID: " << profile->GetId() << "\n";
    AddEnrollmentsToTextIndependentProfile(client, profile);
    SpeakerIdentify(profile, recognizer);
    // Delete the profile.
    client->DeleteProfileAsync(profile);
}

TextDependentVerification および TextIndependentVerification 関数と同様に、この関数では、CreateProfileAsync メソッドを使用して VoiceProfile オブジェクトを作成します。

この場合は、VoiceProfileType::TextIndependentIdentificationCreateProfileAsync に渡します。

その後、2 つのヘルパー関数を呼び出します。1 つは既に定義した AddEnrollmentsToTextIndependentProfile、もう 1 つは次に定義する SpeakerIdentify です。 最後に、DeleteProfileAsync を呼び出してプロファイルをクリーンアップします。

SpeakerIdentify 関数

次のように SpeakerIdentify 関数を定義します。

void SpeakerIdentify(shared_ptr<VoiceProfile> profile, shared_ptr<SpeakerRecognizer> recognizer)
{
    shared_ptr<SpeakerIdentificationModel> model = SpeakerIdentificationModel::FromProfiles({ profile });
    // Note: We need at least four seconds of audio after pauses are subtracted.
    std::cout << "Please speak for at least ten seconds to identify who it is from your list of enrolled speakers.\n";
    shared_ptr<SpeakerRecognitionResult> result = recognizer->RecognizeOnceAsync(model).get();
    std::cout << "The most similar voice profile is: " << result->ProfileId << " with similarity score: " << result->GetScore() << ".\n\n";
}

この関数では、SpeakerIdentificationModel::FromProfiles メソッドを使用して SpeakerIdentificationModel オブジェクトを作成します。 SpeakerIdentificationModel::FromProfiles は、VoiceProfile オブジェクトのリストを受け取ります。 この場合は、先ほど作成した VoiceProfile オブジェクトを渡します。 必要に応じて、それぞれ、異なる音声のオーディオ サンプルで登録されている複数の VoiceProfile オブジェクトを渡すことができます。

次に、SpeechRecognizer::RecognizeOnceAsync によって、もう一度話すよう求められます。 今回は、自分の声が登録済みの音声プロファイルと比較され、最も類似した音声プロファイルが返されます。

main 関数

最後に、次のように main 関数を定義します。

int main()
{
    auto speech_config = GetSpeechConfig();
    auto client = VoiceProfileClient::FromConfig(speech_config);
    auto recognizer = SpeakerRecognizer::FromConfig(speech_config, audio_config);
    TextDependentVerification(client, recognizer);
    TextIndependentVerification(client, recognizer);
    TextIndependentIdentification(client, recognizer);
    std::cout << "End of quickstart.\n";
}

この関数は、前に定義した関数を呼び出します。 最初に、VoiceProfileClient オブジェクトと SpeakerRecognizer オブジェクトを作成します。

auto speech_config = GetSpeechConfig();
auto client = VoiceProfileClient::FromConfig(speech_config);
auto recognizer = SpeakerRecognizer::FromConfig(speech_config, audio_config);

VoiceProfileClient オブジェクトは、音声プロファイルの作成、登録、および削除に使用されます。 SpeakerRecognizer オブジェクトは、1 つ以上の登録済み音声プロファイルに対して音声サンプルを検証するために使用されます。

オーディオ入力の種類を変更する

この記事の例では、オーディオ サンプルの入力として既定のデバイス マイクを使用しています。 マイク入力の代わりにオーディオ ファイルを使用する必要のあるシナリオでは、次の行を変更します。

auto audio_config = Audio::AudioConfig::FromDefaultMicrophoneInput();

この行を次のように変更します。

auto audio_config = Audio::AudioConfig::FromWavFileInput("path/to/your/file.wav");

または、使用しているすべての audio_configAudio::AudioConfig::FromWavFileInput に置き換えます。 登録用にはマイクを、認証用にはファイルを使用して、入力の種類を使い分けることもできます。

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

このクイックスタートでは、Speech SDK を使用して、話者認識の基本的な設計パターンを学習します。これには、次のものが含まれます。

  • テキスト依存およびテキスト非依存の認証。
  • 音声のグループから音声サンプルを識別する話者識別。
  • 音声プロファイルの削除。

話者認識の概念の概要については、概要に関するページを参照してください。 サポートされているプラットフォームの一覧については、左側のナビゲーションの参照ノードを参照してください。

重要

Microsoft では、話者認識へのアクセスを制限しています。 話者認識を使用するには、Azure AI Speaker Recognition 制限付きアクセス レビュー フォームを使用して申し込んでください。 承認後、Speaker Recognition API にアクセスできます。

前提条件

環境をセットアップする

Speech SDK for Go をインストールします。 これ以上要件がないか、SDK のインストール ガイドを確認してください

独立した識別を実行する

以下の手順に従って、新しい GO モジュールを作成します。

  1. 新しいモジュールを作成するコマンド プロンプトを開き、independent-identification.go という名前の新しいファイルを作成します。

  2. independent-identification.go の内容を以下のコードに置き換えます。

    package main
    
    import (
        "bufio"
        "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/speaker"
        "github.com/Microsoft/cognitive-services-speech-sdk-go/speech"
    )
    
    func GetNewVoiceProfileFromClient(client *speaker.VoiceProfileClient, expectedType common.VoiceProfileType) *speaker.VoiceProfile {
        future := client.CreateProfileAsync(expectedType, "en-US")
        outcome := <-future
        if outcome.Failed() {
            fmt.Println("Got an error creating profile: ", outcome.Error.Error())
            return nil
        }
        profile := outcome.Profile
        _, err := profile.Id()
        if err != nil {
            fmt.Println("Unexpected error creating profile id: ", err)
            return nil
        }
        profileType, err := profile.Type();
        if err != nil {
            fmt.Println("Unexpected error getting profile type: ", err)
            return nil
        }
        if profileType != expectedType {
            fmt.Println("Profile type does not match expected type")
            return nil
        }
        return profile
    }
    
    func EnrollProfile(client *speaker.VoiceProfileClient, profile *speaker.VoiceProfile, audioConfig *audio.AudioConfig) {
        enrollmentReason, currentReason := common.EnrollingVoiceProfile, common.EnrollingVoiceProfile
        var currentResult *speaker.VoiceProfileEnrollmentResult
        expectedEnrollmentCount := 1
        for currentReason == enrollmentReason {
            fmt.Println(`Please speak the following phrase: "I'll talk for a few seconds so you can recognize my voice in the future."`)
            enrollFuture := client.EnrollProfileAsync(profile, audioConfig)
            enrollOutcome := <-enrollFuture
            if enrollOutcome.Failed() {
                fmt.Println("Got an error enrolling profile: ", enrollOutcome.Error.Error())
                return
            }
            currentResult = enrollOutcome.Result
            currentReason = currentResult.Reason
            if currentResult.EnrollmentsCount != expectedEnrollmentCount {
                fmt.Println("Unexpected enrollments for profile: ", currentResult.RemainingEnrollmentsCount)
            }
            expectedEnrollmentCount += 1
        }
        if currentReason != common.EnrolledVoiceProfile {
            fmt.Println("Unexpected result enrolling profile: ", currentResult)
        }
    }
    
    func DeleteProfile(client *speaker.VoiceProfileClient, profile *speaker.VoiceProfile) {
        deleteFuture := client.DeleteProfileAsync(profile)
        deleteOutcome := <-deleteFuture
        if deleteOutcome.Failed() {
            fmt.Println("Got an error deleting profile: ", deleteOutcome.Error.Error())
            return
        }
        result := deleteOutcome.Result
        if result.Reason != common.DeletedVoiceProfile {
            fmt.Println("Unexpected result deleting profile: ", result)
        }
    }
    
    func main() {
        subscription :=  "YourSubscriptionKey"
        region := "YourServiceRegion"
        config, err := speech.NewSpeechConfigFromSubscription(subscription, region)
        if err != nil {
            fmt.Println("Got an error: ", err)
            return
        }
        defer config.Close()
        client, err := speaker.NewVoiceProfileClientFromConfig(config)
        if err != nil {
            fmt.Println("Got an error: ", err)
            return
        }
        defer client.Close()
        audioConfig, err := audio.NewAudioConfigFromDefaultMicrophoneInput()
        if err != nil {
            fmt.Println("Got an error: ", err)
            return
        }
        defer audioConfig.Close()
        <-time.After(10 * time.Second)
        expectedType := common.VoiceProfileType(1)
    
        profile := GetNewVoiceProfileFromClient(client, expectedType)
        if profile == nil {
            fmt.Println("Error creating profile")
            return
        }
        defer profile.Close()
    
        EnrollProfile(client, profile, audioConfig)
    
        profiles := []*speaker.VoiceProfile{profile}
        model, err := speaker.NewSpeakerIdentificationModelFromProfiles(profiles)
        if err != nil {
            fmt.Println("Error creating Identification model: ", err)
        }
        if model == nil {
            fmt.Println("Error creating Identification model: nil model")
            return
        }
        identifyAudioConfig, err := audio.NewAudioConfigFromDefaultMicrophoneInput()
        if err != nil {
            fmt.Println("Got an error: ", err)
            return
        }
        defer identifyAudioConfig.Close()
        speakerRecognizer, err := speaker.NewSpeakerRecognizerFromConfig(config, identifyAudioConfig)
        if err != nil {
            fmt.Println("Got an error: ", err)
            return
        }
        identifyFuture := speakerRecognizer.IdentifyOnceAsync(model)
        identifyOutcome := <-identifyFuture
        if identifyOutcome.Failed() {
            fmt.Println("Got an error identifying profile: ", identifyOutcome.Error.Error())
            return
        }
        identifyResult := identifyOutcome.Result
        if identifyResult.Reason != common.RecognizedSpeakers {
            fmt.Println("Got an unexpected result identifying profile: ", identifyResult)
        }
        expectedID, _ := profile.Id()
        if identifyResult.ProfileID != expectedID {
            fmt.Println("Got an unexpected profile id identifying profile: ", identifyResult.ProfileID)
        }
        if identifyResult.Score < 1.0 {
            fmt.Println("Got an unexpected score identifying profile: ", identifyResult.Score)
        }
    
        DeleteProfile(client, profile)
        bufio.NewReader(os.Stdin).ReadBytes('\n')
    }
    
  3. independent-identification.go 内で、YourSubscriptionKey を実際の音声リソース キーに置き換えて、YourServiceRegion を実際の音声リソース リージョンに置き換えます。

    重要

    終わったらコードからキーを削除し、公開しないよう注意してください。 運用環境では、Azure Key Vault などの資格情報を格納してアクセスする安全な方法を使用します。 詳細については、Azure AI サービスのセキュリティに関する記事を参照してください。

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

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

次に、コードをビルドして実行します。

go build
go run independent-identification

独立した認証を実行する

以下の手順に従って、新しい GO モジュールを作成します。

  1. 新しいモジュールを作成するコマンド プロンプトを開き、independent-verification.go という名前の新しいファイルを作成します。

  2. independent-verification.go の内容を以下のコードに置き換えます。

    package main
    
    import (
        "bufio"
        "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/speaker"
        "github.com/Microsoft/cognitive-services-speech-sdk-go/speech"
    )
    
    func GetNewVoiceProfileFromClient(client *speaker.VoiceProfileClient, expectedType common.VoiceProfileType) *speaker.VoiceProfile {
        future := client.CreateProfileAsync(expectedType, "en-US")
        outcome := <-future
        if outcome.Failed() {
            fmt.Println("Got an error creating profile: ", outcome.Error.Error())
            return nil
        }
        profile := outcome.Profile
        _, err := profile.Id()
        if err != nil {
            fmt.Println("Unexpected error creating profile id: ", err)
            return nil
        }
        profileType, err := profile.Type();
        if err != nil {
            fmt.Println("Unexpected error getting profile type: ", err)
            return nil
        }
        if profileType != expectedType {
            fmt.Println("Profile type does not match expected type")
            return nil
        }
        return profile
    }
    
    func EnrollProfile(client *speaker.VoiceProfileClient, profile *speaker.VoiceProfile, audioConfig *audio.AudioConfig) {
        enrollmentReason, currentReason := common.EnrollingVoiceProfile, common.EnrollingVoiceProfile
        var currentResult *speaker.VoiceProfileEnrollmentResult
        expectedEnrollmentCount := 1
        for currentReason == enrollmentReason {
            fmt.Println(`Please speak the following phrase: "I'll talk for a few seconds so you can recognize my voice in the future."`)
            enrollFuture := client.EnrollProfileAsync(profile, audioConfig)
            enrollOutcome := <-enrollFuture
            if enrollOutcome.Failed() {
                fmt.Println("Got an error enrolling profile: ", enrollOutcome.Error.Error())
                return
            }
            currentResult = enrollOutcome.Result
            currentReason = currentResult.Reason
            if currentResult.EnrollmentsCount != expectedEnrollmentCount {
                fmt.Println("Unexpected enrollments for profile: ", currentResult.RemainingEnrollmentsCount)
            }
            expectedEnrollmentCount += 1
        }
        if currentReason != common.EnrolledVoiceProfile {
            fmt.Println("Unexpected result enrolling profile: ", currentResult)
        }
    }
    
    func DeleteProfile(client *speaker.VoiceProfileClient, profile *speaker.VoiceProfile) {
        deleteFuture := client.DeleteProfileAsync(profile)
        deleteOutcome := <-deleteFuture
        if deleteOutcome.Failed() {
            fmt.Println("Got an error deleting profile: ", deleteOutcome.Error.Error())
            return
        }
        result := deleteOutcome.Result
        if result.Reason != common.DeletedVoiceProfile {
            fmt.Println("Unexpected result deleting profile: ", result)
        }
    }
    
    func main() {
        subscription :=  "YourSubscriptionKey"
        region := "YourServiceRegion"
        config, err := speech.NewSpeechConfigFromSubscription(subscription, region)
        if err != nil {
            fmt.Println("Got an error: ", err)
            return
        }
        defer config.Close()
        client, err := speaker.NewVoiceProfileClientFromConfig(config)
        if err != nil {
            fmt.Println("Got an error: ", err)
            return
        }
        defer client.Close()
        audioConfig, err := audio.NewAudioConfigFromDefaultMicrophoneInput()
        if err != nil {
            fmt.Println("Got an error: ", err)
            return
        }
        defer audioConfig.Close()
        <-time.After(10 * time.Second)
        expectedType := common.VoiceProfileType(3)
    
        profile := GetNewVoiceProfileFromClient(client, expectedType)
        if profile == nil {
            fmt.Println("Error creating profile")
            return
        }
        defer profile.Close()
    
        EnrollProfile(client, profile, audioConfig)
    
        model, err := speaker.NewSpeakerVerificationModelFromProfile(profile)
        if err != nil {
            fmt.Println("Error creating Verification model: ", err)
        }
        if model == nil {
            fmt.Println("Error creating Verification model: nil model")
            return
        }
        verifyAudioConfig, err := audio.NewAudioConfigFromDefaultMicrophoneInput()
        if err != nil {
            fmt.Println("Got an error: ", err)
            return
        }
        defer verifyAudioConfig.Close()
        speakerRecognizer, err := speaker.NewSpeakerRecognizerFromConfig(config, verifyAudioConfig)
        if err != nil {
            fmt.Println("Got an error: ", err)
            return
        }
        verifyFuture := speakerRecognizer.VerifyOnceAsync(model)
        verifyOutcome := <-verifyFuture
        if verifyOutcome.Failed() {
            fmt.Println("Got an error verifying profile: ", verifyOutcome.Error.Error())
            return
        }
        verifyResult := verifyOutcome.Result
        if verifyResult.Reason != common.RecognizedSpeaker {
            fmt.Println("Got an unexpected result verifying profile: ", verifyResult)
        }
        expectedID, _ := profile.Id()
        if verifyResult.ProfileID != expectedID {
            fmt.Println("Got an unexpected profile id verifying profile: ", verifyResult.ProfileID)
        }
        if verifyResult.Score < 1.0 {
            fmt.Println("Got an unexpected score verifying profile: ", verifyResult.Score)
        }
    
        DeleteProfile(client, profile)
        bufio.NewReader(os.Stdin).ReadBytes('\n')
    }
    
  3. independent-verification.go 内で、YourSubscriptionKey を実際の音声リソース キーに置き換えて、YourServiceRegion を実際の音声リソース リージョンに置き換えます。

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

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

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

go build
go run independent-verification

リソースをクリーンアップする

Azure portal または Azure コマンドライン インターフェイス (CLI) を使用して、作成した音声リソースを削除できます。

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

Speech SDK for Java では、話者認識がサポートされていますが、本書には、まだガイドが含まれていません。 作業を開始するには、別のプログラミング言語を選択して概念について学ぶか、この記事の冒頭でリンクされている、Java のリファレンスとサンプルを参照してください。

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

このクイックスタートでは、Speech SDK を使用して、話者認識の基本的な設計パターンを学習します。これには、次のものが含まれます。

  • テキスト依存およびテキスト非依存の認証。
  • 音声のグループから音声サンプルを識別する話者識別。
  • 音声プロファイルの削除。

話者認識の概念の概要については、概要に関するページを参照してください。 サポートされているプラットフォームの一覧については、左側のナビゲーションの参照ノードを参照してください。

重要

Microsoft では、話者認識へのアクセスを制限しています。 話者認識を使用するには、Azure AI Speaker Recognition 制限付きアクセス レビュー フォームを使用して申し込んでください。 承認後、Speaker Recognition API にアクセスできます。

前提条件

Speech SDK のインストール

開始する前に、Speech SDK for JavaScript をインストールする必要があります。

ターゲット環境によっては、次のいずれかを使用します。

Speech SDK for JavaScript microsoft.cognitiveservices.speech.sdk.bundle.js ファイルをダウンロードして抽出します。 HTML ファイルからアクセスできるフォルダーに配置します。

<script src="microsoft.cognitiveservices.speech.sdk.bundle.js"></script>;

ヒント

Web ブラウザーを対象とし、<script> タグを使用している場合は、sdk プレフィックスは必要ありません。 sdk プレフィックスは、require モジュールに名前を付けるために使用されるエイリアスです。

依存関係のインポート

この記事の例を実行するには、.js ファイルの先頭に次のステートメントを追加します。

"use strict";

/* To run this sample, install:
npm install microsoft-cognitiveservices-speech-sdk
*/
var sdk = require("microsoft-cognitiveservices-speech-sdk");
var fs = require("fs");

// Note: Change the locale if desired.
const profile_locale = "en-us";

/* Note: passphrase_files and verify_file should contain paths to audio files that contain \"My voice is my passport, verify me.\"
You can obtain these files from:
https://github.com/Azure-Samples/cognitive-services-speech-sdk/tree/fa6428a0837779cbeae172688e0286625e340942/quickstart/javascript/node/speaker-recognition/verification
*/ 
const passphrase_files = ["myVoiceIsMyPassportVerifyMe01.wav", "myVoiceIsMyPassportVerifyMe02.wav", "myVoiceIsMyPassportVerifyMe03.wav"];
const verify_file = "myVoiceIsMyPassportVerifyMe04.wav";
/* Note: identify_file should contain a path to an audio file that uses the same voice as the other files, but contains different speech. You can obtain this file from:
https://github.com/Azure-Samples/cognitive-services-speech-sdk/tree/fa6428a0837779cbeae172688e0286625e340942/quickstart/javascript/node/speaker-recognition/identification
*/
const identify_file = "aboutSpeechSdk.wav";

var subscription_key = 'PASTE_YOUR_SPEECH_SUBSCRIPTION_KEY_HERE';
var region = 'PASTE_YOUR_SPEECH_ENDPOINT_REGION_HERE';

const ticks_per_second = 10000000;

これらのステートメントでは、必要なライブラリをインポートし、環境変数から音声サービスのサブスクリプション キーとリージョンを取得します。 また、次のタスクで使用するオーディオ ファイルへのパスも指定します。

重要

終わったらコードからキーを削除し、公開しないよう注意してください。 運用環境では、Azure Key Vault などの資格情報を格納してアクセスする安全な方法を使用します。 詳細については、Azure AI サービスのセキュリティに関する記事を参照してください。

ヘルパー関数を作成する

次のヘルパー関数を追加して、音声サービスで使用するためにオーディオ ファイルをストリームに読み込みます。

function GetAudioConfigFromFile (file)
{
    return sdk.AudioConfig.fromWavFileInput(fs.readFileSync(file));
}

この関数では、AudioInputStream.createPushStream および AudioConfig.fromStreamInput メソッドを使用して、AudioConfig オブジェクトを作成します。 この AudioConfig オブジェクトは、オーディオ ストリームを表します。 次のタスクでは、これらの AudioConfig オブジェクトのいくつかを使用します。

テキスト依存の認証

話者認証では、話者の音声が、既知または登録済みの音声と一致することを確認します。 最初の手順は、音声プロファイルの登録です。これは、サービスで、今後の音声サンプルとの比較に使用されます。 この例では、テキスト依存の戦略を使用してプロファイルを登録します。これには、登録と認証に使用する特定のパスフレーズが必要です。 サポートされるパスフレーズの一覧については、リファレンス ドキュメントを参照してください。

TextDependentVerification 関数

まず、TextDependentVerification 関数を作成します。

async function TextDependentVerification(client, speech_config)
{
    console.log ("Text Dependent Verification:\n");
    var profile = null;
    try {
        const type = sdk.VoiceProfileType.TextDependentVerification;
        // Create the profile.
        profile = await client.createProfileAsync(type, profile_locale);
        console.log ("Created profile ID: " + profile.profileId);
        // Get the activation phrases
        await GetActivationPhrases(type, profile_locale);
        await AddEnrollmentsToTextDependentProfile(client, profile, passphrase_files);
        const audio_config = GetAudioConfigFromFile(verify_file);
        const recognizer = new sdk.SpeakerRecognizer(speech_config, audio_config);
        await SpeakerVerify(profile, recognizer);
    }
    catch (error) {
        console.log ("Error:\n" + error);
    }
    finally {
        if (profile !== null) {
            console.log ("Deleting profile ID: " + profile.profileId);
            const deleteResult = await client.deleteProfileAsync (profile);
        }
    }
}

この関数では、VoiceProfileClient.createProfileAsync メソッドを使用して VoiceProfile オブジェクトを作成します。 次の 3 種類VoiceProfile があります。

  • TextIndependentIdentification
  • TextDependentVerification
  • TextIndependentVerification

この場合は、VoiceProfileType.TextDependentVerificationVoiceProfileClient.createProfileAsync に渡します。

その後、次に定義する 2 つのヘルパー関数 AddEnrollmentsToTextDependentProfileSpeakerVerify を呼び出します。 最後に、VoiceProfileClient.deleteProfileAsync を呼び出してプロファイルを削除します。

AddEnrollmentsToTextDependentProfile 関数

次の関数を定義して音声プロファイルを登録します。

async function AddEnrollmentsToTextDependentProfile(client, profile, audio_files)
{
    try {
        for (const file of audio_files) {
            console.log ("Adding enrollment to text dependent profile...");
            const audio_config = GetAudioConfigFromFile(file);
            const result = await client.enrollProfileAsync(profile, audio_config);
            if (result.reason === sdk.ResultReason.Canceled) {
                throw(JSON.stringify(sdk.VoiceProfileEnrollmentCancellationDetails.fromResult(result)));
            }
            else {
                console.log ("Remaining enrollments needed: " + result.privDetails["remainingEnrollmentsCount"] + ".");
            }
        };
        console.log ("Enrollment completed.\n");
    } catch (error) {
        console.log ("Error adding enrollments: " + error);
    }
}

この関数では、前に定義した GetAudioConfigFromFile 関数を呼び出して、オーディオ サンプルから AudioConfig オブジェクトを作成します。 これらのオーディオ サンプルには、"my voice is my passport, verify me." などのパスフレーズが含まれています。その後、VoiceProfileClient.enrollProfileAsync メソッドを使用して、これらのオーディオ サンプルを登録します。

SpeakerVerify 関数

次のように SpeakerVerify を定義します。

async function SpeakerVerify(profile, recognizer)
{
    try {
        const model = sdk.SpeakerVerificationModel.fromProfile(profile);
        const result = await recognizer.recognizeOnceAsync(model);
        console.log ("Verified voice profile for speaker: " + result.profileId + ". Score is: " + result.score + ".\n");
    } catch (error) {
        console.log ("Error verifying speaker: " + error);
    }
}

この関数では、SpeakerVerificationModel.FromProfile メソッドを使用して SpeakerVerificationModel オブジェクトを作成し、先ほど作成した VoiceProfile オブジェクトを渡します。

次に、SpeechRecognizer.recognizeOnceAsync メソッドを呼び出して、前に登録したオーディオ サンプルと同じパスフレーズを含むオーディオ サンプルを認証します。 SpeechRecognizer.recognizeOnceAsyncSpeakerRecognitionResult オブジェクトを返します。このオブジェクトの score プロパティには、0.0 ~ 1.0 の範囲の類似性スコアが含まれています。 また、SpeakerRecognitionResult オブジェクトには、ResultReason 型の reason プロパティも含まれています。 認証が成功した場合は、reason プロパティの値が RecognizedSpeaker になります。

テキストに依存しない認証

テキストに依存する認証とは対照的に、テキストに依存しない認証は次のようになります。

  • 特定のパスフレーズを読み上げる必要はありません。 任意の読み上げが可能です。
  • 3 つのオーディオ サンプルは必要ありませんが、合計 20 秒のオーディオが 必要です。

TextIndependentVerification 関数

まず、TextIndependentVerification 関数を作成します。

async function TextIndependentVerification(client, speech_config)
{
    console.log ("Text Independent Verification:\n");
    var profile = null;
    try {
        const type = sdk.VoiceProfileType.TextIndependentVerification;
        // Create the profile.
        profile = await client.createProfileAsync(type, profile_locale);
        console.log ("Created profile ID: " + profile.profileId);
        // Get the activation phrases
        await GetActivationPhrases(type, profile_locale);
        await AddEnrollmentsToTextIndependentProfile(client, profile, [identify_file]);
        const audio_config = GetAudioConfigFromFile(passphrase_files[0]);
        const recognizer = new sdk.SpeakerRecognizer(speech_config, audio_config);
        await SpeakerVerify(profile, recognizer);
    }
    catch (error) {
        console.log ("Error:\n" + error);
    }
    finally {
        if (profile !== null) {
            console.log ("Deleting profile ID: " + profile.profileId);
            const deleteResult = await client.deleteProfileAsync (profile);
        }
    }
}

TextDependentVerification 関数と同様に、この関数では、VoiceProfileClient.createProfileAsync メソッドを使用して VoiceProfile オブジェクトを作成します。

この場合は、VoiceProfileType.TextIndependentVerificationcreateProfileAsync に渡します。

その後、2 つのヘルパー関数を呼び出します。1 つは次に定義する AddEnrollmentsToTextIndependentProfile、もう 1 つは既に定義した SpeakerVerify です。 最後に、VoiceProfileClient.deleteProfileAsync を呼び出してプロファイルを削除します。

AddEnrollmentsToTextIndependentProfile

次の関数を定義して音声プロファイルを登録します。

async function AddEnrollmentsToTextIndependentProfile(client, profile, audio_files)
{
    try {
        for (const file of audio_files) {
            console.log ("Adding enrollment to text independent profile...");
            const audio_config = GetAudioConfigFromFile(file);
            const result = await client.enrollProfileAsync (profile, audio_config);
            if (result.reason === sdk.ResultReason.Canceled) {
                throw(JSON.stringify(sdk.VoiceProfileEnrollmentCancellationDetails.fromResult(result)));
            }
            else {
                console.log ("Remaining audio time needed: " + (result.privDetails["remainingEnrollmentsSpeechLength"] / ticks_per_second) + " seconds.");
            }
        }
        console.log ("Enrollment completed.\n");
    } catch (error) {
        console.log ("Error adding enrollments: " + error);
    }
}

この関数では、前に定義した GetAudioConfigFromFile 関数を呼び出して、オーディオ サンプルから AudioConfig オブジェクトを作成します。 その後、VoiceProfileClient.enrollProfileAsync メソッドを使用して、これらのオーディオ サンプルを登録します。

話者識別

話者識別を使用して、登録されている音声の特定のグループのが話しているのかを判別します。 このプロセスは、テキスト非依存検証に似ています。 主な違いは、1 つのプロファイルに対して検証するのではなく、複数の音声プロファイルに対して同時に検証する機能です。

TextIndependentIdentification 関数

まず、TextIndependentIdentification 関数を作成します。

async function TextIndependentIdentification(client, speech_config)
{
    console.log ("Text Independent Identification:\n");
    var profile = null;
    try {
        const type = sdk.VoiceProfileType.TextIndependentIdentification;
        // Create the profile.
        profile = await client.createProfileAsync(type, profile_locale);
        console.log ("Created profile ID: " + profile.profileId);
        // Get the activation phrases
        await GetActivationPhrases(type, profile_locale);
        await AddEnrollmentsToTextIndependentProfile(client, profile, [identify_file]);
        const audio_config = GetAudioConfigFromFile(passphrase_files[0]);
        const recognizer = new sdk.SpeakerRecognizer(speech_config, audio_config);
        await SpeakerIdentify(profile, recognizer);
    }
    catch (error) {
        console.log ("Error:\n" + error);
    }
    finally {
        if (profile !== null) {
            console.log ("Deleting profile ID: " + profile.profileId);
            const deleteResult = await client.deleteProfileAsync (profile);
        }
    }
}

TextDependentVerification および TextIndependentVerification 関数と同様に、この関数では、VoiceProfileClient.createProfileAsync メソッドを使用して VoiceProfile オブジェクトを作成します。

この場合は、VoiceProfileType.TextIndependentIdentificationVoiceProfileClient.createProfileAsync に渡します。

その後、2 つのヘルパー関数を呼び出します。1 つは既に定義した AddEnrollmentsToTextIndependentProfile、もう 1 つは次に定義する SpeakerIdentify です。 最後に、VoiceProfileClient.deleteProfileAsync を呼び出してプロファイルを削除します。

SpeakerIdentify 関数

次のように SpeakerIdentify 関数を定義します。

async function SpeakerIdentify(profile, recognizer)
{
    try {
        const model = sdk.SpeakerIdentificationModel.fromProfiles([profile]);
        const result = await recognizer.recognizeOnceAsync(model);
        console.log ("The most similar voice profile is: " + result.profileId + " with similarity score: " + result.score + ".\n");
    } catch (error) {
        console.log ("Error identifying speaker: " + error);
    }
}

この関数では、SpeakerIdentificationModel.fromProfiles メソッドを使用して SpeakerIdentificationModel オブジェクトを作成し、先ほど作成した VoiceProfile オブジェクトを渡します。

次に、SpeechRecognizer.recognizeOnceAsync メソッドを呼び出して、オーディオ サンプルを渡します。 SpeechRecognizer.recognizeOnceAsync は、SpeakerIdentificationModel の作成に使用した VoiceProfile オブジェクトに基づいて、このオーディオ サンプルの音声の識別を試みます。 これにより、SpeakerRecognitionResult オブジェクトが返されます。その profileId プロパティでは、一致する VoiceProfile が識別され (存在する場合)、score プロパティには、0.0 ~ 1.0 の範囲の類似性スコアが含まれています。

main 関数

最後に、次のように main 関数を定義します。

async function main() {
    const speech_config = sdk.SpeechConfig.fromSubscription(subscription_key, region);
    const client = new sdk.VoiceProfileClient(speech_config);

    await TextDependentVerification(client, speech_config);
    await TextIndependentVerification(client, speech_config);
    await TextIndependentIdentification(client, speech_config);
    console.log ("End of quickstart.");
}
main();

この関数では、音声プロファイルの作成、登録、および削除に使用される VoiceProfileClient オブジェクトを作成します。 その後、前に定義した関数を呼び出します。

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

Speech SDK for Objective-C では、話者認識がサポートされていません。 別のプログラミング言語を選択するか、この記事の冒頭でリンクされている、Objective-C のリファレンスとサンプルを使用してください。

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

Speech SDK for Swift では、話者認識がサポートされていません。 別のプログラミング言語を選択するか、この記事の冒頭でリンクされている、Swift のリファレンスとサンプルを使用してください。

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

Speech SDK for Python では、話者認識がサポートされていません。 別のプログラミング言語を選択するか、この記事の冒頭でリンクされている、Python のリファレンスとサンプルを使用してください。

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

このクイックスタートでは、Speech SDK を使用して、話者認識の基本的な設計パターンを学習します。これには、次のものが含まれます。

  • テキスト依存およびテキスト非依存の認証。
  • 音声のグループから音声サンプルを識別する話者識別。
  • 音声プロファイルの削除。

話者認識の概念の概要については、概要に関するページを参照してください。 サポートされているプラットフォームの一覧については、左側のナビゲーションの参照ノードを参照してください。

重要

Microsoft では、話者認識へのアクセスを制限しています。 話者認識を使用するには、Azure AI Speaker Recognition 制限付きアクセス レビュー フォームを使用して申し込んでください。 承認後、Speaker Recognition API にアクセスできます。

前提条件

テキスト依存の認証

話者認証では、話者の音声が、既知または登録済みの音声と一致することを確認します。 最初の手順は、音声プロファイルの登録です。これは、サービスで、今後の音声サンプルとの比較に使用されます。 この例では、テキスト依存の戦略を使用してプロファイルを登録します。これには、登録と認証に使用する特定のパスフレーズが必要です。 サポートされるパスフレーズの一覧については、リファレンス ドキュメントを参照してください。

まず、音声プロファイルを作成します。 この記事の各 curl コマンドに、音声サービスのサブスクリプション キーとリージョンを挿入する必要があります。

重要

終わったらコードからキーを削除し、公開しないよう注意してください。 運用環境では、Azure Key Vault などの資格情報を格納してアクセスする安全な方法を使用します。 詳細については、Azure AI サービスのセキュリティに関する記事を参照してください。

# Note Change locale if needed.
curl --location --request POST 'INSERT_ENDPOINT_HERE/speaker-recognition/verification/text-dependent/profiles?api-version=2021-09-05' \
--header 'Ocp-Apim-Subscription-Key: INSERT_SUBSCRIPTION_KEY_HERE' \
--header 'Content-Type: application/json' \
--data-raw '{
    '\''locale'\'':'\''en-us'\''
}'

音声プロファイルには、次の 3 種類があります。

  • テキスト依存の認証
  • テキストに依存しない認証
  • テキストに依存しない識別

この場合は、テキスト依存の認証音声プロファイルを作成します。 次の応答が返されるはずです。

{
    "remainingEnrollmentsCount": 3,
    "locale": "en-us",
    "createdDateTime": "2020-09-29T14:54:29.683Z",
    "enrollmentStatus": "Enrolling",
    "modelVersion": null,
    "profileId": "714ce523-de76-4220-b93f-7c1cc1882d6e",
    "lastUpdatedDateTime": null,
    "enrollmentsCount": 0,
    "enrollmentsLength": 0.0,
    "enrollmentSpeechLength": 0.0
}

次に、音声プロファイルを登録します。 --data-binary パラメーター値には、サポートされているパスフレーズのいずれか ("my voice is my passport, verify me." など) が含まれている、お使いのコンピューター上のオーディオ ファイルを指定します。オーディオ ファイルは、Windows Voice Recorder などのアプリで録音できます。 または、テキスト読み上げを使用して生成することもできます。

curl --location --request POST 'INSERT_ENDPOINT_HERE/speaker-recognition/verification/text-dependent/profiles/INSERT_PROFILE_ID_HERE/enrollments?api-version=2021-09-05' \
--header 'Ocp-Apim-Subscription-Key: INSERT_SUBSCRIPTION_KEY_HERE' \
--header 'Content-Type: audio/wav' \
--data-binary @'INSERT_FILE_PATH_HERE'

次の応答が返されるはずです。

{
    "remainingEnrollmentsCount": 2,
    "passPhrase": "my voice is my passport verify me",
    "profileId": "714ce523-de76-4220-b93f-7c1cc1882d6e",
    "enrollmentStatus": "Enrolling",
    "enrollmentsCount": 1,
    "enrollmentsLength": 3.5,
    "enrollmentsSpeechLength": 2.88,
    "audioLength": 3.5,
    "audioSpeechLength": 2.88
}

この応答は、さらに 2 つのオーディオ サンプルを登録する必要があることを示します。

合計 3 つのオーディオ サンプルを登録した後は、次の応答が返されます。

{
    "remainingEnrollmentsCount": 0,
    "passPhrase": "my voice is my passport verify me",
    "profileId": "714ce523-de76-4220-b93f-7c1cc1882d6e",
    "enrollmentStatus": "Enrolled",
    "enrollmentsCount": 3,
    "enrollmentsLength": 10.5,
    "enrollmentsSpeechLength": 8.64,
    "audioLength": 3.5,
    "audioSpeechLength": 2.88
}

これで、音声プロファイルに対してオーディオ サンプルを認証する準備が整いました。 このオーディオ サンプルには、音声プロファイルの登録に使用したサンプルと同じパスフレーズが含まれている必要があります。

curl --location --request POST 'INSERT_ENDPOINT_HERE/speaker-recognition/verification/text-dependent/profiles/INSERT_PROFILE_ID_HERE:verify?api-version=2021-09-05' \
--header 'Ocp-Apim-Subscription-Key: INSERT_SUBSCRIPTION_KEY_HERE' \
--header 'Content-Type: audio/wav' \
--data-binary @'INSERT_FILE_PATH_HERE'

次の応答が返されるはずです。

{
    "recognitionResult": "Accept",
    "score": 1.0
}

Accept は、パスフレーズが一致し、認証が成功したことを意味します。 応答には、0.0 ~ 1.0 の範囲の類似性スコアも含まれています。

終了するには、音声プロファイルを削除します。

curl --location --request DELETE \
'INSERT_ENDPOINT_HERE/speaker-recognition/verification/text-dependent/profiles/INSERT_PROFILE_ID_HERE?api-version=2021-09-05' \
--header 'Ocp-Apim-Subscription-Key: INSERT_SUBSCRIPTION_KEY_HERE'

応答はありません。

テキストに依存しない認証

テキストに依存する認証とは対照的に、テキストに依存しない認証は次のようになります。

  • 特定のパスフレーズを読み上げる必要はありません。 任意の読み上げが可能です。
  • 3 つのオーディオ サンプルは必要ありませんが、合計 20 秒のオーディオが 必要です。

まず、テキストに依存しない認証プロファイルを作成します。

curl --location --request POST 'INSERT_ENDPOINT_HERE/speaker-recognition/verification/text-independent/profiles?api-version=2021-09-05' \
--header 'Ocp-Apim-Subscription-Key: INSERT_SUBSCRIPTION_KEY_HERE' \
--header 'Content-Type: application/json' \
--data-raw '{
    '\''locale'\'':'\''en-us'\''
}'

次の応答が返されるはずです。

{
    "profileStatus": "Inactive",
    "remainingEnrollmentsSpeechLength": 20.0,
    "profileId": "3f85dca9-ffc9-4011-bf21-37fad2beb4d2",
    "locale": "en-us",
    "enrollmentStatus": "Enrolling",
    "createdDateTime": "2020-09-29T16:08:52.409Z",
    "lastUpdatedDateTime": null,
    "enrollmentsCount": 0,
    "enrollmentsLength": 0.0,
    "enrollmentSpeechLength": 0.0
    "modelVersion": null,
}

次に、音声プロファイルを登録します。 ここでも、3 つのオーディオ サンプルを送信するのではなく、合計 20 秒のオーディオを含むオーディオ サンプルを送信する必要があります。

curl --location --request POST 'INSERT_ENDPOINT_HERE/speaker-recognition/verification/text-independent/profiles/INSERT_PROFILE_ID_HERE/enrollments?api-version=2021-09-05' \
--header 'Ocp-Apim-Subscription-Key: INSERT_SUBSCRIPTION_KEY_HERE' \
--header 'Content-Type: audio/wav' \
--data-binary @'INSERT_FILE_PATH_HERE'

十分なオーディオ サンプルを送信した後は、次の応答が返されます。

{
    "remainingEnrollmentsSpeechLength": 0.0,
    "profileId": "3f85dca9-ffc9-4011-bf21-37fad2beb4d2",
    "enrollmentStatus": "Enrolled",
    "enrollmentsCount": 1,
    "enrollmentsLength": 33.16,
    "enrollmentsSpeechLength": 29.21,
    "audioLength": 33.16,
    "audioSpeechLength": 29.21
}

これで、音声プロファイルに対してオーディオ サンプルを認証する準備が整いました。 この場合も、このオーディオ サンプルがパスフレーズが含んでいる必要はありません。 任意の音声を含めることができますが、合計で少なくとも 4 秒のオーディオが含まれる必要があります。

curl --location --request POST 'INSERT_ENDPOINT_HERE/speaker-recognition/verification/text-independent/profiles/INSERT_PROFILE_ID_HERE:verify?api-version=2021-09-05' \
--header 'Ocp-Apim-Subscription-Key: INSERT_SUBSCRIPTION_KEY_HERE' \
--header 'Content-Type: audio/wav' \
--data-binary @'INSERT_FILE_PATH_HERE'

次の応答が返されるはずです。

{
    "recognitionResult": "Accept",
    "score": 0.9196669459342957
}

Accept は、認証が成功したことを意味します。 応答には、0.0 ~ 1.0 の範囲の類似性スコアも含まれています。

終了するには、音声プロファイルを削除します。

curl --location --request DELETE 'INSERT_ENDPOINT_HERE/speaker-recognition/verification/text-independent/profiles/INSERT_PROFILE_ID_HERE?api-version=2021-09-05' \
--header 'Ocp-Apim-Subscription-Key: INSERT_SUBSCRIPTION_KEY_HERE'

応答はありません。

話者識別

話者識別を使用して、登録されている音声の特定のグループのが話しているのかを判別します。 このプロセスは、テキスト非依存検証に似ています。 主な違いは、1 つのプロファイルに対して検証するのではなく、複数の音声プロファイルに対して同時に検証する機能です。

まず、テキストに依存しない識別プロファイルを作成します。

# Note Change locale if needed.
curl --location --request POST 'INSERT_ENDPOINT_HERE/speaker-recognition/identification/text-independent/profiles?api-version=2021-09-05' \
--header 'Ocp-Apim-Subscription-Key: INSERT_SUBSCRIPTION_KEY_HERE' \
--header 'Content-Type: application/json' \
--data-raw '{
    '\''locale'\'':'\''en-us'\''
}'

次の応答が返されるはずです。

{
    "profileStatus": "Inactive",
    "remainingEnrollmentsSpeechLengthInSec": 20.0,
    "profileId": "de99ab38-36c8-4b82-b137-510907c61fe8",
    "locale": "en-us",
    "enrollmentStatus": "Enrolling",
    "createdDateTime": "2020-09-22T17:25:48.642Z",
    "lastUpdatedDateTime": null,
    "enrollmentsCount": 0,
    "enrollmentsLengthInSec": 0.0,
    "enrollmentsSpeechLengthInSec": 0.0,
    "modelVersion": null
}

次に、音声プロファイルを登録します。 ここでも、合計 20 秒のオーディオを含むオーディオ サンプルを送信する必要があります。 これらのサンプルにパスフレーズを含める必要はありません。

curl --location --request POST 'INSERT_ENDPOINT_HERE/speaker-recognition/identification/text-independent/profiles/INSERT_PROFILE_ID_HERE/enrollments?api-version=2021-09-05' \
--header 'Ocp-Apim-Subscription-Key: INSERT_SUBSCRIPTION_KEY_HERE' \
--header 'Content-Type: audio/wav' \
--data-binary @'INSERT_FILE_PATH_HERE'

十分なオーディオ サンプルを送信した後は、次の応答が返されます。

{
    "remainingEnrollmentsSpeechLength": 0.0,
    "profileId": "de99ab38-36c8-4b82-b137-510907c61fe8",
    "enrollmentStatus": "Enrolled",
    "enrollmentsCount": 2,
    "enrollmentsLength": 36.69,
    "enrollmentsSpeechLength": 31.95,
    "audioLength": 33.16,
    "audioSpeechLength": 29.21
}

これで、音声プロファイルを使用してオーディオ サンプルを識別する準備が整いました。 識別コマンドは、使用可能な音声プロファイル ID のコンマ区切りリストを受け取ります。 この場合は、先ほど作成した音声プロファイルの ID を渡します。 必要に応じて、複数の音声プロファイルの ID を渡すことができます。この場合、各音声プロファイルは、異なる音声のオーディオ サンプルに登録されています。

# Profile ids comma seperated list
curl --location --request POST 'INSERT_ENDPOINT_HERE/speaker-recognition/identification/text-independent/profiles:identifySingleSpeaker?api-version=2021-09-05&profileIds=INSERT_PROFILE_ID_HERE' \
--header 'Ocp-Apim-Subscription-Key: INSERT_SUBSCRIPTION_KEY_HERE' \
--header 'Content-Type: audio/wav' \
--data-binary @'INSERT_FILE_PATH_HERE'

次の応答が返されるはずです。

Success:
{
    "identifiedProfile": {
        "profileId": "de99ab38-36c8-4b82-b137-510907c61fe8",
        "score": 0.9083486
    },
    "profilesRanking": [
        {
            "profileId": "de99ab38-36c8-4b82-b137-510907c61fe8",
            "score": 0.9083486
        }
    ]
}

この応答には、送信したオーディオ サンプルに最も合致する音声プロファイルの ID が含まれています。 また、類似性の順でランク付けされた、候補となる音声プロファイルのリストも含まれています。

終了するには、音声プロファイルを削除します。

curl --location --request DELETE \
'INSERT_ENDPOINT_HERE/speaker-recognition/identification/text-independent/profiles/INSERT_PROFILE_ID_HERE?api-version=2021-09-05' \
--header 'Ocp-Apim-Subscription-Key: INSERT_SUBSCRIPTION_KEY_HERE'

応答はありません。

Speech CLI では、話者認識がサポートされていますが、本書には、まだガイドが含まれていません。 使用を開始し、概念について学ぶには、別のプログラミング言語を選択してください。

次のステップ