Entrée vocale dans DirectX

Notes

Cet article concerne les API natives WinRT héritées. Pour les nouveaux projets d’application native, nous vous recommandons d’utiliser l’API OpenXR.

Cet article explique comment implémenter des commandes vocales ainsi que la reconnaissance de petites phrases et de phrases dans une application DirectX pour Windows Mixed Reality.

Notes

Les extraits de code de cet article utilisent C++/CX plutôt que C++/WinRT conforme à C++/WinRT, qui est utilisé dans le modèle de projet holographique C++. Les concepts sont équivalents pour un projet C++/WinRT, mais vous devez traduire le code.

Utiliser SpeechRecognizer pour la reconnaissance vocale continue

Cette section explique comment utiliser la reconnaissance vocale continue pour activer les commandes vocales dans votre application. Cette procédure utilise le code de l’exemple HolographicVoiceInput . Lorsque l’exemple est en cours d’exécution, parlez le nom de l’une des commandes de couleur inscrites pour modifier la couleur du cube tournant.

Tout d’abord, créez un instance Windows::Media::SpeechRecognizer.

À partir de HolographicVoiceInputSampleMain::CreateSpeechConstraintsForCurrentState :

m_speechRecognizer = ref new SpeechRecognizer();

Créez une liste de commandes vocales que le module de reconnaissance doit écouter. Ici, nous construisons un ensemble de commandes pour modifier la couleur d’un hologramme. Pour plus de commodité, nous créons également les données que nous utiliserons ultérieurement pour les commandes.

m_speechCommandList = ref new Platform::Collections::Vector<String^>();
   m_speechCommandData.clear();
   m_speechCommandList->Append(StringReference(L"white"));
   m_speechCommandData.push_back(float4(1.f, 1.f, 1.f, 1.f));
   m_speechCommandList->Append(StringReference(L"grey"));
   m_speechCommandData.push_back(float4(0.5f, 0.5f, 0.5f, 1.f));
   m_speechCommandList->Append(StringReference(L"green"));
   m_speechCommandData.push_back(float4(0.f, 1.f, 0.f, 1.f));
   m_speechCommandList->Append(StringReference(L"black"));
   m_speechCommandData.push_back(float4(0.1f, 0.1f, 0.1f, 1.f));
   m_speechCommandList->Append(StringReference(L"red"));
   m_speechCommandData.push_back(float4(1.f, 0.f, 0.f, 1.f));
   m_speechCommandList->Append(StringReference(L"yellow"));
   m_speechCommandData.push_back(float4(1.f, 1.f, 0.f, 1.f));
   m_speechCommandList->Append(StringReference(L"aquamarine"));
   m_speechCommandData.push_back(float4(0.f, 1.f, 1.f, 1.f));
   m_speechCommandList->Append(StringReference(L"blue"));
   m_speechCommandData.push_back(float4(0.f, 0.f, 1.f, 1.f));
   m_speechCommandList->Append(StringReference(L"purple"));
   m_speechCommandData.push_back(float4(1.f, 0.f, 1.f, 1.f));

Vous pouvez utiliser des mots phonétiques qui ne se trouvent peut-être pas dans un dictionnaire pour spécifier des commandes.

m_speechCommandList->Append(StringReference(L"SpeechRecognizer"));
   m_speechCommandData.push_back(float4(0.5f, 0.1f, 1.f, 1.f));

Pour charger la liste des commandes dans la liste des contraintes du module de reconnaissance vocale, utilisez un objet SpeechRecognitionListConstraint .

SpeechRecognitionListConstraint^ spConstraint = ref new SpeechRecognitionListConstraint(m_speechCommandList);
   m_speechRecognizer->Constraints->Clear();
   m_speechRecognizer->Constraints->Append(spConstraint);
   create_task(m_speechRecognizer->CompileConstraintsAsync()).then([this](SpeechRecognitionCompilationResult^ compilationResult)
   {
       if (compilationResult->Status == SpeechRecognitionResultStatus::Success)
       {
           m_speechRecognizer->ContinuousRecognitionSession->StartAsync();
       }
       else
       {
           // Handle errors here.
       }
   });

Abonnez-vous à l’événement ResultGenerated sur la session SpeechContinuousRecognitionSession du module de reconnaissance vocale. Cet événement avertit votre application quand l’une de vos commandes a été reconnue.

m_speechRecognizer->ContinuousRecognitionSession->ResultGenerated +=
       ref new TypedEventHandler<SpeechContinuousRecognitionSession^, SpeechContinuousRecognitionResultGeneratedEventArgs^>(
           std::bind(&HolographicVoiceInputSampleMain::OnResultGenerated, this, _1, _2)
           );

Votre gestionnaire d’événements OnResultGenerated reçoit des données d’événement dans un instance SpeechContinuousRecognitionResultGeneratedEventArgs. Si la confiance est supérieure au seuil que vous avez défini, votre application doit noter que l’événement s’est produit. Enregistrez les données d’événement afin de pouvoir les utiliser dans une boucle de mise à jour ultérieure.

À partir de HolographicVoiceInputSampleMain.cpp :

// Change the cube color, if we get a valid result.
   void HolographicVoiceInputSampleMain::OnResultGenerated(SpeechContinuousRecognitionSession ^sender, SpeechContinuousRecognitionResultGeneratedEventArgs ^args)
   {
       if (args->Result->RawConfidence > 0.5f)
       {
           m_lastCommand = args->Result->Text;
       }
   }

Dans notre exemple de code, nous changeons la couleur du cube hologramme en fonction de la commande de l’utilisateur.

À partir de HolographicVoiceInputSampleMain::Update :

// Check for new speech input since the last frame.
   if (m_lastCommand != nullptr)
   {
       auto command = m_lastCommand;
       m_lastCommand = nullptr;

       int i = 0;
       for each (auto& iter in m_speechCommandList)
       {
           if (iter == command)
           {
               m_spinningCubeRenderer->SetColor(m_speechCommandData[i]);
               break;
           }

           ++i;
       }
   }

Utiliser la reconnaissance « one-shot »

Vous pouvez configurer un module de reconnaissance vocale pour écouter les expressions ou les phrases que l’utilisateur parle. Dans ce cas, nous appliquons une fonction SpeechRecognitionTopicConstraint qui indique au module de reconnaissance vocale le type d’entrée à attendre. Voici un workflow d’application pour ce scénario :

  1. Votre application crée le SpeechRecognizer, fournit des invites d’interface utilisateur et commence à écouter une commande vocale.
  2. L’utilisateur parle une expression ou une phrase.
  3. La reconnaissance vocale de l’utilisateur se produit et un résultat est retourné à l’application. À ce stade, votre application doit fournir une invite d’interface utilisateur pour indiquer que la reconnaissance s’est produite.
  4. En fonction du niveau de confiance auquel vous souhaitez répondre et du niveau de confiance du résultat de la reconnaissance vocale, votre application peut traiter le résultat et répondre comme il convient.

Cette section explique comment créer un SpeechRecognizer, compiler la contrainte et écouter l’entrée vocale.

Le code suivant compile la contrainte de rubrique, qui dans ce cas est optimisée pour la recherche web.

auto constraint = ref new SpeechRecognitionTopicConstraint(SpeechRecognitionScenario::WebSearch, L"webSearch");
   m_speechRecognizer->Constraints->Clear();
   m_speechRecognizer->Constraints->Append(constraint);
   return create_task(m_speechRecognizer->CompileConstraintsAsync())
       .then([this](task<SpeechRecognitionCompilationResult^> previousTask)
   {

Si la compilation réussit, nous pouvons continuer avec la reconnaissance vocale.

try
       {
           SpeechRecognitionCompilationResult^ compilationResult = previousTask.get();

           // Check to make sure that the constraints were in a proper format and the recognizer was able to compile it.
           if (compilationResult->Status == SpeechRecognitionResultStatus::Success)
           {
               // If the compilation succeeded, we can start listening for the user's spoken phrase or sentence.
               create_task(m_speechRecognizer->RecognizeAsync()).then([this](task<SpeechRecognitionResult^>& previousTask)
               {

Le résultat est ensuite retourné à l’application. Si nous sommes suffisamment confiants dans le résultat, nous pouvons traiter la commande. Cet exemple de code traite les résultats avec au moins une confiance moyenne.

try
                   {
                       auto result = previousTask.get();

                       if (result->Status != SpeechRecognitionResultStatus::Success)
                       {
                           PrintWstringToDebugConsole(
                               std::wstring(L"Speech recognition was not successful: ") +
                               result->Status.ToString()->Data() +
                               L"\n"
                               );
                       }

                       // In this example, we look for at least medium confidence in the speech result.
                       if ((result->Confidence == SpeechRecognitionConfidence::High) ||
                           (result->Confidence == SpeechRecognitionConfidence::Medium))
                       {
                           // If the user said a color name anywhere in their phrase, it will be recognized in the
                           // Update loop; then, the cube will change color.
                           m_lastCommand = result->Text;

                           PrintWstringToDebugConsole(
                               std::wstring(L"Speech phrase was: ") +
                               m_lastCommand->Data() +
                               L"\n"
                               );
                       }
                       else
                       {
                           PrintWstringToDebugConsole(
                               std::wstring(L"Recognition confidence not high enough: ") +
                               result->Confidence.ToString()->Data() +
                               L"\n"
                               );
                       }
                   }

Chaque fois que vous utilisez la reconnaissance vocale, watch pour les exceptions qui peuvent indiquer que l’utilisateur a désactivé le microphone dans les paramètres de confidentialité du système. Cela peut se produire pendant l’initialisation ou la reconnaissance.

catch (Exception^ exception)
                   {
                       // Note that if you get an "Access is denied" exception, you might need to enable the microphone
                       // privacy setting on the device and/or add the microphone capability to your app manifest.

                       PrintWstringToDebugConsole(
                           std::wstring(L"Speech recognizer error: ") +
                           exception->ToString()->Data() +
                           L"\n"
                           );
                   }
               });

               return true;
           }
           else
           {
               OutputDebugStringW(L"Could not initialize predefined grammar speech engine!\n");

               // Handle errors here.
               return false;
           }
       }
       catch (Exception^ exception)
       {
           // Note that if you get an "Access is denied" exception, you might need to enable the microphone
           // privacy setting on the device and/or add the microphone capability to your app manifest.

           PrintWstringToDebugConsole(
               std::wstring(L"Exception while trying to initialize predefined grammar speech engine:") +
               exception->Message->Data() +
               L"\n"
               );

           // Handle exceptions here.
           return false;
       }
   });

Notes

Il existe plusieurs speechRecognitionScenarios prédéfinis que vous pouvez utiliser pour optimiser la reconnaissance vocale.

  • Pour optimiser la dictée, utilisez le scénario de dictée.

    // Compile the dictation topic constraint, which optimizes for speech dictation.
    auto dictationConstraint = ref new SpeechRecognitionTopicConstraint(SpeechRecognitionScenario::Dictation, "dictation");
    m_speechRecognizer->Constraints->Append(dictationConstraint);
    
  • Pour les recherches web vocales, utilisez la contrainte de scénario spécifique au web suivante.

    // Add a web search topic constraint to the recognizer.
    auto webSearchConstraint = ref new SpeechRecognitionTopicConstraint(SpeechRecognitionScenario::WebSearch, "webSearch");
    speechRecognizer->Constraints->Append(webSearchConstraint);
    
  • Utilisez la contrainte de formulaire pour remplir des formulaires. Dans ce cas, il est préférable d’appliquer votre propre grammaire optimisée pour remplir le formulaire.

    // Add a form constraint to the recognizer.
    auto formConstraint = ref new SpeechRecognitionTopicConstraint(SpeechRecognitionScenario::FormFilling, "formFilling");
    speechRecognizer->Constraints->Append(formConstraint );
    
  • Vous pouvez fournir votre propre grammaire au format SRGS.

Utiliser la reconnaissance continue

Pour le scénario de dictée continue, consultez l’exemple de code vocal UWP Windows 10.

Gérer la dégradation de la qualité

Les conditions environnementales interfèrent parfois avec la reconnaissance vocale. Par exemple, la salle peut être trop bruyante ou l’utilisateur peut parler trop fort. Dans la mesure du possible, l’API de reconnaissance vocale fournit des informations sur les conditions qui ont provoqué la dégradation de la qualité. Ces informations sont envoyées à votre application via un événement WinRT. L’exemple suivant montre comment s’abonner à cet événement.

m_speechRecognizer->RecognitionQualityDegrading +=
       ref new TypedEventHandler<SpeechRecognizer^, SpeechRecognitionQualityDegradingEventArgs^>(
           std::bind(&HolographicVoiceInputSampleMain::OnSpeechQualityDegraded, this, _1, _2)
           );

Dans notre exemple de code, nous écrivons les informations de conditions dans la console de débogage. Une application peut souhaiter fournir des commentaires à l’utilisateur via l’interface utilisateur, la synthèse vocale et une autre méthode. Il peut également être nécessaire de se comporter différemment lorsque la parole est interrompue par une réduction temporaire de la qualité.

void HolographicSpeechPromptSampleMain::OnSpeechQualityDegraded(SpeechRecognizer^ recognizer, SpeechRecognitionQualityDegradingEventArgs^ args)
   {
       switch (args->Problem)
       {
       case SpeechRecognitionAudioProblem::TooFast:
           OutputDebugStringW(L"The user spoke too quickly.\n");
           break;

       case SpeechRecognitionAudioProblem::TooSlow:
           OutputDebugStringW(L"The user spoke too slowly.\n");
           break;

       case SpeechRecognitionAudioProblem::TooQuiet:
           OutputDebugStringW(L"The user spoke too softly.\n");
           break;

       case SpeechRecognitionAudioProblem::TooLoud:
           OutputDebugStringW(L"The user spoke too loudly.\n");
           break;

       case SpeechRecognitionAudioProblem::TooNoisy:
           OutputDebugStringW(L"There is too much noise in the signal.\n");
           break;

       case SpeechRecognitionAudioProblem::NoSignal:
           OutputDebugStringW(L"There is no signal.\n");
           break;

       case SpeechRecognitionAudioProblem::None:
       default:
           OutputDebugStringW(L"An error was reported with no information.\n");
           break;
       }
   }

Si vous n’utilisez pas de classes ref pour créer votre application DirectX, vous devez vous désabonner de l’événement avant de libérer ou de recréer votre module de reconnaissance vocale. HolographicSpeechPromptSample a une routine pour arrêter la reconnaissance et se désabonner des événements.

Concurrency::task<void> HolographicSpeechPromptSampleMain::StopCurrentRecognizerIfExists()
   {
       return create_task([this]()
       {
           if (m_speechRecognizer != nullptr)
           {
               return create_task(m_speechRecognizer->StopRecognitionAsync()).then([this]()
               {
                   m_speechRecognizer->RecognitionQualityDegrading -= m_speechRecognitionQualityDegradedToken;

                   if (m_speechRecognizer->ContinuousRecognitionSession != nullptr)
                   {
                       m_speechRecognizer->ContinuousRecognitionSession->ResultGenerated -= m_speechRecognizerResultEventToken;
                   }
               });
           }
           else
           {
               return create_task([this]() { m_speechRecognizer = nullptr; });
           }
       });
   }

Utiliser la synthèse vocale pour fournir des invites audibles

Les exemples de parole holographique utilisent la synthèse vocale pour fournir des instructions audibles à l’utilisateur. Cette section montre comment créer un exemple de voix synthétisée, puis le lire via les API audio HRTF.

Nous vous recommandons de fournir vos propres invites vocales lorsque vous demandez une entrée d’expression. Les invites peuvent également aider à indiquer quand les commandes vocales peuvent être parlées pour un scénario de reconnaissance continue. L’exemple suivant montre comment utiliser un synthétiseur vocal pour ce faire. Vous pouvez également utiliser un clip vocal préenregistré, une interface utilisateur visuelle ou un autre indicateur de ce qu’il faut dire, par exemple dans les scénarios où l’invite n’est pas dynamique.

Tout d’abord, créez l’objet SpeechSynthesizer.

auto speechSynthesizer = ref new Windows::Media::SpeechSynthesis::SpeechSynthesizer();

Vous avez également besoin d’une chaîne qui inclut le texte à synthétiser.

// Phrase recognition works best when requesting a phrase or sentence.
   StringReference voicePrompt = L"At the prompt: Say a phrase, asking me to change the cube to a specific color.";

La voix est synthétisée de manière asynchrone via SynthétiserTextToStreamAsync. Ici, nous commençons une tâche asynchrone pour synthétiser la parole.

create_task(speechSynthesizer->SynthesizeTextToStreamAsync(voicePrompt), task_continuation_context::use_current())
       .then([this, speechSynthesizer](task<Windows::Media::SpeechSynthesis::SpeechSynthesisStream^> synthesisStreamTask)
   {
       try
       {

La synthèse vocale est envoyée sous forme de flux d’octets. Nous pouvons utiliser ce flux d’octets pour initialiser une voix XAudio2. Pour nos exemples de code holographique, nous le limons en tant qu’effet audio HRTF.

Windows::Media::SpeechSynthesis::SpeechSynthesisStream^ stream = synthesisStreamTask.get();

           auto hr = m_speechSynthesisSound.Initialize(stream, 0);
           if (SUCCEEDED(hr))
           {
               m_speechSynthesisSound.SetEnvironment(HrtfEnvironment::Small);
               m_speechSynthesisSound.Start();

               // Amount of time to pause after the audio prompt is complete, before listening
               // for speech input.
               static const float bufferTime = 0.15f;

               // Wait until the prompt is done before listening.
               m_secondsUntilSoundIsComplete = m_speechSynthesisSound.GetDuration() + bufferTime;
               m_waitingForSpeechPrompt = true;
           }
       }

Comme pour la reconnaissance vocale, la synthèse vocale lève une exception en cas de problème.

catch (Exception^ exception)
       {
           PrintWstringToDebugConsole(
               std::wstring(L"Exception while trying to synthesize speech: ") +
               exception->Message->Data() +
               L"\n"
               );

           // Handle exceptions here.
       }
   });

Voir aussi