Röstinmatning i DirectX
Anteckning
Den här artikeln handlar om äldre inbyggda WinRT-API:er. För nya interna appprojekt rekommenderar vi att du använder OpenXR-API:et.
Den här artikeln beskriver hur du implementerar röstkommandon plus småfras- och meningsigenkänning i en DirectX-app för Windows Mixed Reality.
Anteckning
Kodfragmenten i den här artikeln använder C++/CX i stället för C++17-kompatibel C++/WinRT, som används i C++-holografiska projektmallen. Begreppen är likvärdiga för ett C++/WinRT-projekt, men du måste översätta koden.
Använda SpeechRecognizer för kontinuerlig taligenkänning
I det här avsnittet beskrivs hur du använder kontinuerlig taligenkänning för att aktivera röstkommandon i din app. Den här genomgången använder kod från HolographicVoiceInput-exemplet . När exemplet körs talar du om namnet på ett av de registrerade färgkommandona för att ändra färgen på den snurrande kuben.
Skapa först en ny Windows::Media::SpeechRecognition::SpeechRecognizer-instans .
Från HolographicVoiceInputSampleMain::CreateSpeechConstraintsForCurrentState:
m_speechRecognizer = ref new SpeechRecognizer();
Skapa en lista med talkommandon som identifieraren ska lyssna efter. Här skapar vi en uppsättning kommandon för att ändra färgen på ett hologram. För enkelhetens skull skapar vi även de data som vi ska använda för kommandona senare.
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));
Du kan använda fonetiska ord som kanske inte finns i en ordlista för att ange kommandon.
m_speechCommandList->Append(StringReference(L"SpeechRecognizer"));
m_speechCommandData.push_back(float4(0.5f, 0.1f, 1.f, 1.f));
Om du vill läsa in listan med kommandon i listan över begränsningar för taligenkänningen använder du ett 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.
}
});
Prenumerera på händelsen ResultGenerated på taligenkänningens SpeechContinuousRecognitionSession. Den här händelsen meddelar din app när ett av dina kommandon har identifierats.
m_speechRecognizer->ContinuousRecognitionSession->ResultGenerated +=
ref new TypedEventHandler<SpeechContinuousRecognitionSession^, SpeechContinuousRecognitionResultGeneratedEventArgs^>(
std::bind(&HolographicVoiceInputSampleMain::OnResultGenerated, this, _1, _2)
);
Din OnResultGenerated-händelsehanterare tar emot händelsedata i en SpeechContinuousRecognitionResultGeneratedEventArgs-instans . Om konfidensen är större än det tröskelvärde som du definierade bör appen notera att händelsen inträffade. Spara händelsedata så att du kan använda dem i en senare uppdateringsloop.
Från 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;
}
}
I vår exempelkod ändrar vi färgen på den snurrande hologramkuben enligt användarens kommando.
Från 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;
}
}
Använda "one-shot"-igenkänning
Du kan konfigurera en taligenkänning för att lyssna efter fraser eller meningar som användaren talar. I det här fallet tillämpar vi en SpeechRecognitionTopicConstraint som talar om för taligenkänningen vilken typ av indata som ska förväntas. Här är ett apparbetsflöde för det här scenariot:
- Din app skapar SpeechRecognizer, tillhandahåller användargränssnittsprompter och börjar lyssna efter ett talat kommando.
- Användaren talar en fras eller mening.
- Igenkänning av användarens tal sker och ett resultat returneras till appen. Nu bör din app ange en UI-uppmaning för att ange att igenkänningen har inträffat.
- Beroende på den konfidensnivå som du vill svara på och konfidensnivån för taligenkänningsresultatet kan appen bearbeta resultatet och svara efter behov.
I det här avsnittet beskrivs hur du skapar en SpeechRecognizer, kompilerar villkoret och lyssnar efter talindata.
Följande kod kompilerar ämnesbegränsningen, som i det här fallet är optimerad för webbsökning.
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)
{
Om kompilering lyckas kan vi fortsätta med taligenkänning.
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)
{
Resultatet returneras sedan till appen. Om vi är tillräckligt säkra på resultatet kan vi bearbeta kommandot. Det här kodexemplet bearbetar resultat med minst medelhög konfidens.
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"
);
}
}
När du använder taligenkänning watch för undantag som kan tyda på att användaren har stängt av mikrofonen i systemets sekretessinställningar. Detta kan inträffa under initiering eller igenkänning.
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;
}
});
Anteckning
Det finns flera fördefinierade SpeechRecognitionScenarios som du kan använda för att optimera taligenkänning.
Om du vill optimera för diktering använder du dikteringsscenariot.
// Compile the dictation topic constraint, which optimizes for speech dictation. auto dictationConstraint = ref new SpeechRecognitionTopicConstraint(SpeechRecognitionScenario::Dictation, "dictation"); m_speechRecognizer->Constraints->Append(dictationConstraint);
Använd följande webbspecifika scenariovillkor för talwebbsökningar.
// Add a web search topic constraint to the recognizer. auto webSearchConstraint = ref new SpeechRecognitionTopicConstraint(SpeechRecognitionScenario::WebSearch, "webSearch"); speechRecognizer->Constraints->Append(webSearchConstraint);
Använd formulärvillkoret för att fylla i formulär. I det här fallet är det bäst att använda din egen grammatik som är optimerad för att fylla i formuläret.
// Add a form constraint to the recognizer. auto formConstraint = ref new SpeechRecognitionTopicConstraint(SpeechRecognitionScenario::FormFilling, "formFilling"); speechRecognizer->Constraints->Append(formConstraint );
Du kan ange din egen grammatik i SRGS-format.
Använda kontinuerlig igenkänning
Information om scenariot med kontinuerlig diktering finns i Windows 10 UWP-talkodexempel.
Hantera kvalitetsförsämring
Miljöförhållanden stör ibland taligenkänning. Rummet kan till exempel vara för bullrigt eller så kan användaren tala för högt. När det är möjligt tillhandahåller API:et för taligenkänning information om de villkor som orsakade kvalitetsförsämringen. Den här informationen skickas till din app via en WinRT-händelse. I följande exempel visas hur du prenumererar på den här händelsen.
m_speechRecognizer->RecognitionQualityDegrading +=
ref new TypedEventHandler<SpeechRecognizer^, SpeechRecognitionQualityDegradingEventArgs^>(
std::bind(&HolographicVoiceInputSampleMain::OnSpeechQualityDegraded, this, _1, _2)
);
I vårt kodexempel skriver vi villkorsinformationen till felsökningskonsolen. En app kanske vill ge feedback till användaren via användargränssnittet, talsyntesen och en annan metod. Eller så kan det behöva bete sig annorlunda när tal avbryts av en tillfällig kvalitetsminskning.
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;
}
}
Om du inte använder referensklasser för att skapa din DirectX-app måste du avbryta prenumerationen på händelsen innan du släpper eller återskapar taligenkänningen. HolographicSpeechPromptSample har en rutin för att stoppa igenkänning och avbryta prenumerationen på händelser.
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; });
}
});
}
Använda talsyntes för att ge hörbara frågor
Holografiska talexempel använder talsyntes för att ge användaren hörbara instruktioner. Det här avsnittet visar hur du skapar ett syntetiserat röstexempel och sedan spelar upp det genom HRTF-ljud-API:erna.
Vi rekommenderar att du anger egna talfrågor när du begär frasinmatning. Frågor kan också hjälpa dig att ange när talkommandon kan talas för ett scenario med kontinuerlig igenkänning. I följande exempel visas hur du använder en talsyntes för att göra detta. Du kan också använda ett förinspelat röstklipp, ett visuellt användargränssnitt eller en annan indikator på vad du ska säga, till exempel i scenarier där prompten inte är dynamisk.
Skapa först SpeechSynthesizer-objektet.
auto speechSynthesizer = ref new Windows::Media::SpeechSynthesis::SpeechSynthesizer();
Du behöver också en sträng som innehåller texten som ska syntetiseras.
// 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.";
Tal syntetiseras asynkront via SynthesizeTextToStreamAsync. Här startar vi en asynkron uppgift för att syntetisera talet.
create_task(speechSynthesizer->SynthesizeTextToStreamAsync(voicePrompt), task_continuation_context::use_current())
.then([this, speechSynthesizer](task<Windows::Media::SpeechSynthesis::SpeechSynthesisStream^> synthesisStreamTask)
{
try
{
Talsyntesen skickas som en byteström. Vi kan använda byteströmmen för att initiera en XAudio2-röst. För våra holografiska kodexempel spelar vi upp det som en HRTF-ljudeffekt.
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;
}
}
Precis som med taligenkänning utlöser talsyntesen ett undantag om något går fel.
catch (Exception^ exception)
{
PrintWstringToDebugConsole(
std::wstring(L"Exception while trying to synthesize speech: ") +
exception->Message->Data() +
L"\n"
);
// Handle exceptions here.
}
});