Wprowadzanie głosu w programie DirectX

Uwaga

Ten artykuł dotyczy starszych natywnych interfejsów API winRT. W przypadku nowych projektów aplikacji natywnych zalecamy używanie interfejsu API OpenXR.

W tym artykule wyjaśniono, jak zaimplementować polecenia głosowe oraz rozpoznawanie małych fraz i zdań w aplikacji DirectX dla Windows Mixed Reality.

Uwaga

Fragmenty kodu w tym artykule używają języka C++/CX, a nie C++17 zgodnego z językiem C++/WinRT, który jest używany w szablonie projektu holograficznego języka C++. Pojęcia są równoważne projektowi C++/WinRT, ale należy przetłumaczyć kod.

Używanie elementu SpeechRecognizer do ciągłego rozpoznawania mowy

W tej sekcji opisano sposób używania ciągłego rozpoznawania mowy do włączania poleceń głosowych w aplikacji. W tym przewodniku użyto kodu z przykładu HolographicVoiceInput . Gdy przykład jest uruchomiony, powiedz nazwę jednego z zarejestrowanych poleceń koloru, aby zmienić kolor modułu wirującego.

Najpierw utwórz nowe wystąpienie Windows::Media::SpeechRecognition::SpeechRecognizer .

Z holographicVoiceInputSampleMain::CreateSpeechConstraintsForCurrentState:

m_speechRecognizer = ref new SpeechRecognizer();

Utwórz listę poleceń mowy do nasłuchiwania przez aparat rozpoznawania. W tym miejscu skonstruujemy zestaw poleceń, aby zmienić kolor hologramu. Dla wygody utworzymy również dane, których będziemy używać dla poleceń później.

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));

Do określania poleceń można użyć słów fonetycznych, które mogą nie znajdować się w słowniku.

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

Aby załadować listę poleceń do listy ograniczeń dla rozpoznawania mowy, użyj obiektu 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.
       }
   });

Zasubskrybuj zdarzenie ResultGenerated w wystąpieniu mowy SpeechContinuousRecognitionSession. To zdarzenie powiadamia aplikację o rozpoznaniu jednego z poleceń.

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

Procedura obsługi zdarzeń OnResultGenerated odbiera dane zdarzeń w wystąpieniu SpeechContinuousRecognitionResultGeneratedEventArgs . Jeśli pewność jest większa niż zdefiniowany próg, aplikacja powinna zauważyć, że wystąpiło zdarzenie. Zapisz dane zdarzenia, aby można było ich używać w późniejszej pętli aktualizacji.

Z 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;
       }
   }

W naszym przykładowym kodzie zmieniamy kolor wirującego modułu hologramu zgodnie z poleceniem użytkownika.

Z 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;
       }
   }

Używanie funkcji rozpoznawania "jeden strzał"

Rozpoznawanie mowy można skonfigurować tak, aby nasłuchiwać fraz lub zdań wypowiadanych przez użytkownika. W tym przypadku stosujemy właściwość SpeechRecognitionTopicConstraint , która informuje aparat rozpoznawania mowy, jakiego typu dane wejściowe mają być oczekiwane. Oto przepływ pracy aplikacji dla tego scenariusza:

  1. Aplikacja tworzy element SpeechRecognizer, udostępnia monity interfejsu użytkownika i rozpoczyna nasłuchiwanie polecenia mówionego.
  2. Użytkownik mówi frazę lub zdanie.
  3. Rozpoznawanie mowy użytkownika występuje, a wynik jest zwracany do aplikacji. W tym momencie aplikacja powinna podać monit interfejsu użytkownika, aby wskazać, że wystąpiło rozpoznawanie.
  4. W zależności od poziomu ufności, na który chcesz reagować, oraz poziomu ufności wyniku rozpoznawania mowy aplikacja może przetworzyć wynik i odpowiednio odpowiedzieć.

W tej sekcji opisano sposób tworzenia elementu SpeechRecognizer, kompilowania ograniczenia i nasłuchiwania danych wejściowych mowy.

Poniższy kod kompiluje ograniczenie tematu, które w tym przypadku jest zoptymalizowane pod kątem wyszukiwania w Internecie.

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)
   {

Jeśli kompilacja zakończy się pomyślnie, możemy kontynuować rozpoznawanie mowy.

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)
               {

Wynik zostanie następnie zwrócony do aplikacji. Jeśli jesteśmy wystarczająco pewni wyniku, możemy przetworzyć polecenie. Ten przykładowy kod przetwarza wyniki z co najmniej średnim zaufaniem.

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"
                               );
                       }
                   }

Za każdym razem, gdy używasz rozpoznawania mowy, watch dla wyjątków, które mogą wskazywać, że użytkownik wyłączył mikrofon w ustawieniach prywatności systemu. Może się to zdarzyć podczas inicjowania lub rozpoznawania.

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;
       }
   });

Uwaga

Istnieje kilka wstępnie zdefiniowanych elementów SpeechRecognitionScenarios , których można użyć do optymalizacji rozpoznawania mowy.

  • Aby zoptymalizować pod kątem dyktowania, użyj scenariusza dyktowania.

    // Compile the dictation topic constraint, which optimizes for speech dictation.
    auto dictationConstraint = ref new SpeechRecognitionTopicConstraint(SpeechRecognitionScenario::Dictation, "dictation");
    m_speechRecognizer->Constraints->Append(dictationConstraint);
    
  • W przypadku wyszukiwania w internecie mowy użyj następującego ograniczenia scenariusza specyficznego dla sieci Web.

    // Add a web search topic constraint to the recognizer.
    auto webSearchConstraint = ref new SpeechRecognitionTopicConstraint(SpeechRecognitionScenario::WebSearch, "webSearch");
    speechRecognizer->Constraints->Append(webSearchConstraint);
    
  • Użyj ograniczenia formularza, aby wypełnić formularze. W takim przypadku najlepiej jest zastosować własną gramatykę zoptymalizowaną pod kątem wypełniania formularza.

    // Add a form constraint to the recognizer.
    auto formConstraint = ref new SpeechRecognitionTopicConstraint(SpeechRecognitionScenario::FormFilling, "formFilling");
    speechRecognizer->Constraints->Append(formConstraint );
    
  • Możesz podać własną gramatykę w formacie SRGS.

Używanie ciągłego rozpoznawania

Aby zapoznać się ze scenariuszem ciągłego dyktowania, zobacz przykładowy kod mowy platformy UWP Windows 10.

Obsługa obniżenia jakości

Warunki środowiskowe czasami zakłócają rozpoznawanie mowy. Na przykład pokój może być zbyt hałaśliwy lub użytkownik może mówić zbyt głośno. Jeśli to możliwe, interfejs API rozpoznawania mowy udostępnia informacje o warunkach, które spowodowały obniżenie jakości. Te informacje są wypychane do aplikacji za pośrednictwem zdarzenia WinRT. W poniższym przykładzie pokazano, jak subskrybować to zdarzenie.

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

W naszym przykładzie kodu zapisujemy informacje o warunkach w konsoli debugowania. Aplikacja może chcieć przekazać użytkownikowi opinię za pośrednictwem interfejsu użytkownika, syntezy mowy i innej metody. Może też być konieczne zachowanie się inaczej, gdy mowa zostanie przerwana przez tymczasowe obniżenie jakości.

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;
       }
   }

Jeśli nie używasz klas ref do tworzenia aplikacji DirectX, przed wydaniem lub ponownym utworzeniem aparatu rozpoznawania mowy musisz anulować subskrypcję zdarzenia. HolographicSpeechPromptSample ma procedurę zatrzymywania rozpoznawania i anulowania subskrypcji zdarzeń.

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; });
           }
       });
   }

Używanie syntezy mowy w celu zapewnienia słyszalnych monitów

Przykłady mowy holograficznej używają syntezy mowy, aby zapewnić użytkownikowi instrukcje słyszalne. W tej sekcji pokazano, jak utworzyć syntetyzowany przykład głosu, a następnie odtworzyć go z powrotem za pomocą interfejsów API audio HRTF.

Zalecamy podanie własnych monitów mowy podczas żądania wprowadzenia frazy. Monity mogą również pomóc wskazać, kiedy polecenia mowy mogą być mówione w scenariuszu ciągłego rozpoznawania. W poniższym przykładzie pokazano, jak używać syntezatora mowy, aby to zrobić. Można również użyć wstępnie nagranego klipu głosowego, wizualnego interfejsu użytkownika lub innego wskaźnika tego, co należy powiedzieć, na przykład w scenariuszach, w których monit nie jest dynamiczny.

Najpierw utwórz obiekt SpeechSynthesizer.

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

Potrzebny jest również ciąg zawierający tekst do syntezy.

// 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.";

Mowa jest syntetyzowana asynchronicznie za pomocą syntetyzowaniaTextToStreamAsync. W tym miejscu rozpoczynamy zadanie asynchroniczne, aby zsyntetyzować mowę.

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

Synteza mowy jest wysyłana jako strumień bajtów. Możemy użyć tego strumienia bajtów, aby zainicjować głos XAudio2. W przypadku naszych przykładów kodu holograficznego odtwarzamy go z powrotem jako efekt 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;
           }
       }

Podobnie jak w przypadku rozpoznawania mowy, synteza mowy zgłasza wyjątek, jeśli coś pójdzie nie tak.

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

           // Handle exceptions here.
       }
   });

Zobacz też