Input suara di DirectX
Catatan
Artikel ini berkaitan dengan API asli WinRT warisan. Untuk proyek aplikasi asli baru, sebaiknya gunakan OpenXR API.
Artikel ini menjelaskan cara menerapkan perintah suara ditambah pengenalan frasa kecil dan kalimat di aplikasi DirectX untuk Windows Mixed Reality.
Catatan
Cuplikan kode dalam artikel ini menggunakan C++/CX daripada C++17-compliant C++/WinRT, yang digunakan dalam templat proyek holografik C++. Konsepnya setara untuk proyek C++/WinRT, tetapi Anda perlu menerjemahkan kode.
Menggunakan SpeechRecognizer untuk pengenalan ucapan berkelanjutan
Bagian ini menjelaskan cara menggunakan pengenalan ucapan berkelanjutan untuk mengaktifkan perintah suara di aplikasi Anda. Panduan ini menggunakan kode dari sampel HolographicVoiceInput . Saat sampel berjalan, ucapkan nama salah satu perintah warna terdaftar untuk mengubah warna kubus berputar.
Pertama, buat instans Windows::Media::SpeechRecognition::SpeechRecognizer baru.
Dari HolographicVoiceInputSampleMain::CreateSpeechConstraintsForCurrentState:
m_speechRecognizer = ref new SpeechRecognizer();
Buat daftar perintah ucapan untuk didengarkan pengenal. Di sini, kami membuat serangkaian perintah untuk mengubah warna hologram. Untuk kenyamanan, kita juga membuat data yang akan kita gunakan untuk perintah nanti.
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));
Anda dapat menggunakan kata fonetik yang mungkin tidak ada dalam kamus untuk menentukan perintah.
m_speechCommandList->Append(StringReference(L"SpeechRecognizer"));
m_speechCommandData.push_back(float4(0.5f, 0.1f, 1.f, 1.f));
Untuk memuat daftar perintah ke dalam daftar batasan untuk pengenal ucapan, gunakan objek 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.
}
});
Berlangganan peristiwa ResultGenerated pada SpeechContinuousRecognitionSession pengenal ucapan. Kejadian ini memberi tahu aplikasi Anda saat salah satu perintah Anda telah dikenali.
m_speechRecognizer->ContinuousRecognitionSession->ResultGenerated +=
ref new TypedEventHandler<SpeechContinuousRecognitionSession^, SpeechContinuousRecognitionResultGeneratedEventArgs^>(
std::bind(&HolographicVoiceInputSampleMain::OnResultGenerated, this, _1, _2)
);
Penanganan aktivitas OnResultGenerated Anda menerima data peristiwa dalam instans SpeechContinuousRecognitionResultGeneratedEventArgs . Jika keyakinan lebih besar dari ambang yang Anda tentukan, aplikasi Anda harus mencatat bahwa peristiwa tersebut terjadi. Simpan data peristiwa sehingga Anda dapat menggunakannya dalam perulangan pembaruan nanti.
Dari 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;
}
}
Dalam contoh kode kami, kami mengubah warna kubus hologram berputar sesuai dengan perintah pengguna.
Dari 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;
}
}
Gunakan pengenalan "satu bidikan"
Anda dapat mengonfigurasi pengenal ucapan untuk mendengarkan frasa atau kalimat yang diucapkan pengguna. Dalam hal ini, kami menerapkan SpeechRecognitionTopicConstraint yang memberi tahu pengenal ucapan jenis input apa yang diharapkan. Berikut adalah alur kerja aplikasi untuk skenario ini:
- Aplikasi Anda membuat SpeechRecognizer, menyediakan perintah UI, dan mulai mendengarkan perintah lisan.
- Pengguna mengucapkan frasa atau kalimat.
- Pengenalan ucapan pengguna terjadi, dan hasilnya dikembalikan ke aplikasi. Pada titik ini, aplikasi Anda harus memberikan perintah UI untuk menunjukkan bahwa pengenalan telah terjadi.
- Bergantung pada tingkat keyakinan yang ingin Anda tanggapi dan tingkat keyakinan hasil pengenalan ucapan, aplikasi Anda dapat memproses hasilnya dan merespons dengan tepat.
Bagian ini menjelaskan cara membuat SpeechRecognizer, mengkompilasi batasan, dan mendengarkan input ucapan.
Kode berikut mengkompilasi batasan topik, yang dalam hal ini dioptimalkan untuk pencarian 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)
{
Jika kompilasi berhasil, kita dapat melanjutkan pengenalan ucapan.
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)
{
Hasilnya kemudian dikembalikan ke aplikasi. Jika kita cukup yakin dalam hasilnya, kita dapat memproses perintah. Contoh kode ini memproses hasil dengan setidaknya keyakinan sedang.
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"
);
}
}
Setiap kali Anda menggunakan pengenalan ucapan, watch untuk pengecualian yang dapat menunjukkan bahwa pengguna telah menonaktifkan mikrofon di pengaturan privasi sistem. Ini dapat terjadi selama inisialisasi atau pengenalan.
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;
}
});
Catatan
Ada beberapa SpeechRecognitionScenarios yang telah ditentukan sebelumnya yang dapat Anda gunakan untuk mengoptimalkan pengenalan ucapan.
Untuk mengoptimalkan dikte, gunakan skenario dikte.
// Compile the dictation topic constraint, which optimizes for speech dictation. auto dictationConstraint = ref new SpeechRecognitionTopicConstraint(SpeechRecognitionScenario::Dictation, "dictation"); m_speechRecognizer->Constraints->Append(dictationConstraint);
Untuk pencarian web ucapan, gunakan batasan skenario khusus web berikut.
// Add a web search topic constraint to the recognizer. auto webSearchConstraint = ref new SpeechRecognitionTopicConstraint(SpeechRecognitionScenario::WebSearch, "webSearch"); speechRecognizer->Constraints->Append(webSearchConstraint);
Gunakan batasan formulir untuk mengisi formulir. Dalam hal ini, yang terbaik adalah menerapkan tata bahasa Anda sendiri yang dioptimalkan untuk mengisi formulir.
// Add a form constraint to the recognizer. auto formConstraint = ref new SpeechRecognitionTopicConstraint(SpeechRecognitionScenario::FormFilling, "formFilling"); speechRecognizer->Constraints->Append(formConstraint );
Anda dapat menyediakan tata bahasa Anda sendiri dalam format SRGS.
Gunakan pengenalan berkelanjutan
Untuk skenario dikte berkelanjutan, lihat sampel kode ucapan Windows 10 UWP.
Menangani degradasi kualitas
Kondisi lingkungan terkadang mengganggu pengenalan ucapan. Misalnya, ruangan mungkin terlalu berisik, atau pengguna mungkin berbicara terlalu keras. Jika memungkinkan, API pengenalan ucapan memberikan informasi tentang kondisi yang menyebabkan penurunan kualitas. Informasi ini didorong ke aplikasi Anda melalui acara WinRT. Contoh berikut menunjukkan cara berlangganan peristiwa ini.
m_speechRecognizer->RecognitionQualityDegrading +=
ref new TypedEventHandler<SpeechRecognizer^, SpeechRecognitionQualityDegradingEventArgs^>(
std::bind(&HolographicVoiceInputSampleMain::OnSpeechQualityDegraded, this, _1, _2)
);
Dalam sampel kode kami, kami menulis informasi kondisi ke konsol debug. Aplikasi mungkin ingin memberikan umpan balik kepada pengguna melalui UI, sintesis ucapan, dan metode lain. Atau mungkin perlu berperilaku berbeda ketika ucapan terganggu oleh pengurangan kualitas sementara.
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;
}
}
Jika Anda tidak menggunakan kelas ref untuk membuat aplikasi DirectX, Anda harus berhenti berlangganan dari acara sebelum merilis atau membuat ulang pengenal ucapan Anda. HolographicSpeechPromptSample memiliki rutinitas untuk menghentikan pengenalan dan berhenti berlangganan dari peristiwa.
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; });
}
});
}
Menggunakan sintesis ucapan untuk memberikan perintah yang dapat didengar
Sampel ucapan holografik menggunakan sintesis ucapan untuk memberikan instruksi yang dapat didengar kepada pengguna. Bagian ini menunjukkan cara membuat sampel suara yang disintesis lalu memutarnya kembali melalui API audio HRTF.
Sebaiknya berikan perintah ucapan Anda sendiri saat Anda meminta input frasa. Perintah juga dapat membantu menunjukkan kapan perintah ucapan dapat diucapkan untuk skenario pengenalan berkelanjutan. Contoh berikut menunjukkan cara menggunakan synthesizer ucapan untuk melakukan ini. Anda juga dapat menggunakan klip suara yang telah direkam sebelumnya, UI visual, atau indikator lain tentang apa yang harus dikatakan, misalnya dalam skenario di mana perintah tidak dinamis.
Pertama, buat objek SpeechSynthesizer.
auto speechSynthesizer = ref new Windows::Media::SpeechSynthesis::SpeechSynthesizer();
Anda juga memerlukan string yang menyertakan teks untuk disintesis.
// 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.";
Ucapan disintesis secara asinkron melalui SynthesizeTextToStreamAsync. Di sini, kita memulai tugas asinkron untuk mensintesis ucapan.
create_task(speechSynthesizer->SynthesizeTextToStreamAsync(voicePrompt), task_continuation_context::use_current())
.then([this, speechSynthesizer](task<Windows::Media::SpeechSynthesis::SpeechSynthesisStream^> synthesisStreamTask)
{
try
{
Sintesis ucapan dikirim sebagai aliran byte. Kita dapat menggunakan aliran byte tersebut untuk menginisialisasi suara XAudio2. Untuk sampel kode holografik kami, kami memutarnya kembali sebagai efek 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;
}
}
Seperti halnya pengenalan ucapan, sintesis ucapan melemparkan pengecualian jika ada yang salah.
catch (Exception^ exception)
{
PrintWstringToDebugConsole(
std::wstring(L"Exception while trying to synthesize speech: ") +
exception->Message->Data() +
L"\n"
);
// Handle exceptions here.
}
});