Hlasový vstup v DirectX
Poznámka
Tento článek se týká starších nativních rozhraní API WinRT. Pro nové projekty nativních aplikací doporučujeme použít rozhraní OpenXR API.
Tento článek vysvětluje, jak implementovat hlasové příkazy a rozpoznávání malých frází a vět v aplikaci DirectX pro Windows Mixed Reality.
Poznámka
Fragmenty kódu v tomto článku používají C++/CX místo C++17 kompatibilní C++/WinRT, který se používá v šabloně holografického projektu C++. Koncepty jsou ekvivalentem pro projekt C++/WinRT, ale musíte kód přeložit.
Použití funkce SpeechRecognizer pro průběžné rozpoznávání řeči
Tato část popisuje, jak používat průběžné rozpoznávání řeči k povolení hlasových příkazů v aplikaci. Tento návod používá kód z ukázky HolographicVoiceInput . Když je ukázka spuštěná, vyslovte název jednoho z registrovaných barevných příkazů a změňte barvu rotující datové krychle.
Nejprve vytvořte novou instanci Windows::Media::SpeechRecognition::SpeechRecognizer .
Z HolographicVoiceInputSampleMain::CreateSpeechConstraintsForCurrentState:
m_speechRecognizer = ref new SpeechRecognizer();
Vytvořte seznam hlasových příkazů, které má rozpoznávání naslouchat. Tady vytvoříme sadu příkazů, které změní barvu hologramu. Pro usnadnění si také vytvoříme data, která použijeme pro příkazy později.
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));
K zadání příkazů můžete použít fonetická slova, která nemusí být ve slovníku.
m_speechCommandList->Append(StringReference(L"SpeechRecognizer"));
m_speechCommandData.push_back(float4(0.5f, 0.1f, 1.f, 1.f));
Chcete-li načíst seznam příkazů do seznamu omezení pro rozpoznávání řeči, použijte SpeechRecognitionListConstraint objekt.
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.
}
});
Přihlaste se k odběru události ResultGenerated v rozpoznávání řeči SpeechContinuousRecognitionSession. Tato událost upozorní vaši aplikaci, když byl rozpoznán jeden z vašich příkazů.
m_speechRecognizer->ContinuousRecognitionSession->ResultGenerated +=
ref new TypedEventHandler<SpeechContinuousRecognitionSession^, SpeechContinuousRecognitionResultGeneratedEventArgs^>(
std::bind(&HolographicVoiceInputSampleMain::OnResultGenerated, this, _1, _2)
);
Obslužná rutina události OnResultGenerated přijímá data událostí v instanci SpeechContinuousRecognitionResultGeneratedEventArgs. Pokud je hodnota spolehlivosti větší než vámi definovaná prahová hodnota, měla by aplikace poznamenat, že k události došlo. Uložte data události, abyste je mohli použít v pozdější smyčce aktualizací.
Pochází 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;
}
}
V našem ukázkovém kódu změníme barvu rotující krychle hologramu podle příkazu uživatele.
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;
}
}
Použití jednorázového rozpoznávání
Rozpoznávání řeči můžete nakonfigurovat tak, aby naslouchal frázím nebo větám, které uživatel vyslovuje. V tomto případě použijeme SpeechRecognitionTopicConstraint , který řekne rozpoznávání řeči, jaký typ vstupu má očekávat. Tady je pracovní postup aplikace pro tento scénář:
- Vaše aplikace vytvoří SpeechRecognizer, zobrazí výzvy uživatelského rozhraní a začne naslouchat mluvenému příkazu.
- Uživatel vysloví frázi nebo větu.
- Rozpozná se řeč uživatele a do aplikace se vrátí výsledek. V tomto okamžiku by vaše aplikace měla poskytnout výzvu uživatelského rozhraní, která indikuje, že došlo k rozpoznání.
- V závislosti na úrovni spolehlivosti, na kterou chcete reagovat, a na úrovni spolehlivosti výsledku rozpoznávání řeči může aplikace zpracovat výsledek a podle potřeby reagovat.
Tato část popisuje, jak vytvořit službu SpeechRecognizer, zkompilovat omezení a naslouchat vstupu řeči.
Následující kód zkompiluje omezení tématu, které je v tomto případě optimalizované pro vyhledávání na webu.
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)
{
Pokud kompilace proběhne úspěšně, můžeme pokračovat v rozpoznávání řeči.
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)
{
Výsledek se pak vrátí do aplikace. Pokud jsme si ve výsledku dostatečně jistí, můžeme příkaz zpracovat. Tento příklad kódu zpracovává výsledky s alespoň střední jistotou.
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"
);
}
}
Kdykoli použijete rozpoznávání řeči, watch výjimky, které můžou naznačovat, že uživatel v nastavení ochrany osobních údajů systému vypnul mikrofon. K tomu může dojít během inicializace nebo rozpoznávání.
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;
}
});
Poznámka
Existuje několik předdefinovaných objektů SpeechRecognitionScenarios , které můžete použít k optimalizaci rozpoznávání řeči.
Pokud chcete optimalizovat pro diktování, použijte scénář diktování.
// Compile the dictation topic constraint, which optimizes for speech dictation. auto dictationConstraint = ref new SpeechRecognitionTopicConstraint(SpeechRecognitionScenario::Dictation, "dictation"); m_speechRecognizer->Constraints->Append(dictationConstraint);
Pro vyhledávání na webu řeči použijte následující omezení scénáře specifické pro web.
// Add a web search topic constraint to the recognizer. auto webSearchConstraint = ref new SpeechRecognitionTopicConstraint(SpeechRecognitionScenario::WebSearch, "webSearch"); speechRecognizer->Constraints->Append(webSearchConstraint);
K vyplňování formulářů použijte omezení formuláře. V takovém případě je nejlepší použít vlastní gramatiku, která je optimalizovaná pro vyplnění formuláře.
// Add a form constraint to the recognizer. auto formConstraint = ref new SpeechRecognitionTopicConstraint(SpeechRecognitionScenario::FormFilling, "formFilling"); speechRecognizer->Constraints->Append(formConstraint );
Vlastní gramatiku můžete zadat ve formátu SRGS.
Použití průběžného rozpoznávání
Scénář průběžného diktování najdete v ukázce kódu řeči Windows 10 UPW.
Zpracování snížení kvality
Okolní podmínky někdy kolidují s rozpoznáváním řeči. Místnost může být například příliš hlučná nebo uživatel může mluvit příliš nahlas. Kdykoli je to možné, poskytuje rozhraní API pro rozpoznávání řeči informace o podmínkách, které způsobily snížení kvality. Tyto informace se do vaší aplikace nasdílí prostřednictvím události WinRT. Následující příklad ukazuje, jak se přihlásit k odběru této události.
m_speechRecognizer->RecognitionQualityDegrading +=
ref new TypedEventHandler<SpeechRecognizer^, SpeechRecognitionQualityDegradingEventArgs^>(
std::bind(&HolographicVoiceInputSampleMain::OnSpeechQualityDegraded, this, _1, _2)
);
V ukázce kódu zapíšeme informace o podmínkách do konzoly ladění. Aplikace může chtít uživateli poskytnout zpětnou vazbu prostřednictvím uživatelského rozhraní, syntézy řeči a jiné metody. Nebo se může muset chovat jinak, když je řeč přerušena dočasným snížením kvality.
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;
}
}
Pokud k vytvoření aplikace DirectX nepoužíváte třídy ref, musíte před uvolněním nebo opětovnou vytvořením rozpoznávání řeči odhlásit odběr události. HolographicSpeechPromptSample má rutinu pro zastavení rozpoznávání a odhlášení odběru událostí.
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; });
}
});
}
Použití syntézy řeči k poskytování zvukových výzev
Holografické ukázky řeči používají syntézu řeči k poskytování zvukových pokynů uživateli. V této části se dozvíte, jak vytvořit syntetizovanou ukázku hlasu a pak ji přehrát prostřednictvím zvukových rozhraní API HRTF.
Při žádosti o zadání fráze doporučujeme zadat vlastní hlasové výzvy. Výzvy vám také můžou pomoct určit, kdy je možné vyslovovat hlasové příkazy pro scénář průběžného rozpoznávání. Následující příklad ukazuje, jak k tomu použít syntezátor řeči. Můžete také použít předem nahraný hlasový klip, vizuální uživatelské rozhraní nebo jiný indikátor toho, co říct, například ve scénářích, kdy výzva není dynamická.
Nejprve vytvořte objekt SpeechSynthesizer.
auto speechSynthesizer = ref new Windows::Media::SpeechSynthesis::SpeechSynthesizer();
Potřebujete také řetězec, který obsahuje text, který se má syntetizovat.
// 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.";
Řeč je syntetizována asynchronně prostřednictvím syntetizaceTextToStreamAsync. Tady spustíme asynchronní úlohu, která syntetizuje řeč.
create_task(speechSynthesizer->SynthesizeTextToStreamAsync(voicePrompt), task_continuation_context::use_current())
.then([this, speechSynthesizer](task<Windows::Media::SpeechSynthesis::SpeechSynthesisStream^> synthesisStreamTask)
{
try
{
Syntéza řeči se odesílá jako datový proud bajtů. Tento bajtový stream můžeme použít k inicializaci hlasu XAudio2. U ukázek holografického kódu ho přehrajeme jako zvukový efekt 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;
}
}
Stejně jako u rozpoznávání řeči vyvolá syntéza řeči výjimku, pokud se něco nepovede.
catch (Exception^ exception)
{
PrintWstringToDebugConsole(
std::wstring(L"Exception while trying to synthesize speech: ") +
exception->Message->Data() +
L"\n"
);
// Handle exceptions here.
}
});