From cf9ac0a5cb8f7ec4840ed5581b2b6d0365c9762c Mon Sep 17 00:00:00 2001 From: Tillman Staffen Date: Thu, 4 Dec 2025 17:41:05 +0100 Subject: [PATCH] Unified Word Lists in STT --- .../AvatarCore_STT/AvatarCore_STT.Build.cs | 29 +- .../Processor/Azure/STTProcessorAzure.cpp | 4 +- .../Processor/Whisper/STTProcessorWhisper.cpp | 438 +++++ .../Whisper/STTWhisperProcessorConfig.cpp | 10 + .../AvatarCore_STT/Private/STTManagerBase.cpp | 17 +- .../Processor/Azure/STTAzureProcessorConfig.h | 2 - .../Public/Processor/STTProcessorBase.h | 2 +- .../Processor/Whisper/STTProcessorWhisper.h | 47 + .../Whisper/STTWhisperProcessorConfig.h | 38 + .../Source/AvatarCore_STT/Public/STTStructs.h | 3 + .../AzureWrapper/include/c_api/CMakeLists.txt | 8 + .../include/c_api/azac_api_c_common.h | 81 + .../include/c_api/azac_api_c_diagnostics.h | 80 + .../include/c_api/azac_api_c_error.h | 24 + .../include/c_api/azac_api_c_pal.h | 13 + .../AzureWrapper/include/c_api/azac_debug.h | 845 +++++++++ .../AzureWrapper/include/c_api/azac_error.h | 455 +++++ .../AzureWrapper/include/c_api/speechapi_c.h | 51 + .../include/c_api/speechapi_c_audio_config.h | 27 + .../speechapi_c_audio_processing_options.h | 173 ++ .../include/c_api/speechapi_c_audio_stream.h | 67 + .../c_api/speechapi_c_audio_stream_format.h | 93 + ...eechapi_c_auto_detect_source_lang_config.h | 15 + .../include/c_api/speechapi_c_common.h | 81 + .../include/c_api/speechapi_c_connection.h | 46 + .../include/c_api/speechapi_c_conversation.h | 28 + ...hapi_c_conversation_transcription_result.h | 11 + .../speechapi_c_conversation_translator.h | 63 + .../include/c_api/speechapi_c_diagnostics.h | 8 + .../c_api/speechapi_c_dialog_service_config.h | 15 + .../speechapi_c_dialog_service_connector.h | 92 + .../speechapi_c_embedded_speech_config.h | 21 + .../include/c_api/speechapi_c_error.h | 9 + .../c_api/speechapi_c_ext_audiocompression.h | 105 + .../include/c_api/speechapi_c_factory.h | 29 + .../include/c_api/speechapi_c_grammar.h | 33 + .../c_api/speechapi_c_hybrid_speech_config.h | 9 + .../c_api/speechapi_c_intent_recognizer.h | 16 + .../include/c_api/speechapi_c_intent_result.h | 11 + .../c_api/speechapi_c_intent_trigger.h | 17 + .../include/c_api/speechapi_c_json.h | 37 + .../speechapi_c_keyword_recognition_model.h | 17 + ...speechapi_c_language_understanding_model.h | 18 + .../include/c_api/speechapi_c_meeting.h | 28 + ...speechapi_c_meeting_transcription_result.h | 12 + .../include/c_api/speechapi_c_operations.h | 12 + .../include/c_api/speechapi_c_participant.h | 15 + .../speechapi_c_pattern_matching_model.h | 33 + ...echapi_c_pronunciation_assessment_config.h | 33 + .../include/c_api/speechapi_c_property_bag.h | 159 ++ .../include/c_api/speechapi_c_recognizer.h | 66 + .../include/c_api/speechapi_c_result.h | 108 ++ .../include/c_api/speechapi_c_session.h | 16 + .../c_api/speechapi_c_source_lang_config.h | 13 + .../c_api/speechapi_c_speaker_recognition.h | 37 + .../include/c_api/speechapi_c_speech_config.h | 171 ++ .../speechapi_c_speech_recognition_model.h | 13 + .../speechapi_c_speech_translation_config.h | 16 + .../speechapi_c_speech_translation_model.h | 14 + .../c_api/speechapi_c_synthesis_request.h | 17 + .../include/c_api/speechapi_c_synthesizer.h | 74 + .../speechapi_c_translation_recognizer.h | 17 + .../c_api/speechapi_c_translation_result.h | 14 + .../include/c_api/speechapi_c_user.h | 13 + .../AzureWrapper/include/c_api/spxdebug.h | 548 ++++++ .../AzureWrapper/include/c_api/spxerror.h | 449 +++++ .../include/cxx_api/CMakeLists.txt | 9 + .../include/cxx_api/azac_api_cxx_common.h | 82 + .../include/cxx_api/speechapi_cxx.h | 117 ++ .../cxx_api/speechapi_cxx_audio_config.h | 338 ++++ .../cxx_api/speechapi_cxx_audio_data_stream.h | 239 +++ .../speechapi_cxx_audio_processing_options.h | 358 ++++ .../cxx_api/speechapi_cxx_audio_stream.h | 995 ++++++++++ .../speechapi_cxx_audio_stream_format.h | 215 +++ ...chapi_cxx_auto_detect_source_lang_config.h | 141 ++ ...chapi_cxx_auto_detect_source_lang_result.h | 85 + .../speechapi_cxx_class_language_model.h | 70 + .../include/cxx_api/speechapi_cxx_common.h | 16 + .../cxx_api/speechapi_cxx_connection.h | 346 ++++ .../speechapi_cxx_connection_eventargs.h | 68 + .../speechapi_cxx_connection_message.h | 152 ++ ...eechapi_cxx_connection_message_eventargs.h | 79 + .../cxx_api/speechapi_cxx_conversation.h | 340 ++++ .../speechapi_cxx_conversation_transcriber.h | 509 +++++ ...cxx_conversation_transcription_eventargs.h | 165 ++ ...pi_cxx_conversation_transcription_result.h | 72 + .../speechapi_cxx_conversation_translator.h | 448 +++++ ...chapi_cxx_conversation_translator_events.h | 262 +++ ...versational_language_understanding_model.h | 89 + .../speechapi_cxx_dialog_service_config.h | 268 +++ .../speechapi_cxx_dialog_service_connector.h | 547 ++++++ ...i_cxx_dialog_service_connector_eventargs.h | 148 ++ .../speechapi_cxx_embedded_speech_config.h | 324 ++++ .../include/cxx_api/speechapi_cxx_enums.h | 1685 +++++++++++++++++ .../cxx_api/speechapi_cxx_event_logger.h | 108 ++ .../include/cxx_api/speechapi_cxx_eventargs.h | 47 + .../cxx_api/speechapi_cxx_eventsignal.h | 202 ++ .../cxx_api/speechapi_cxx_eventsignalbase.h | 166 ++ .../cxx_api/speechapi_cxx_file_logger.h | 115 ++ .../include/cxx_api/speechapi_cxx_grammar.h | 70 + .../cxx_api/speechapi_cxx_grammar_list.h | 90 + .../cxx_api/speechapi_cxx_grammar_phrase.h | 64 + .../speechapi_cxx_hybrid_speech_config.h | 161 ++ ...eechapi_cxx_intent_recognition_eventargs.h | 169 ++ .../speechapi_cxx_intent_recognition_result.h | 119 ++ .../cxx_api/speechapi_cxx_intent_recognizer.h | 513 +++++ .../cxx_api/speechapi_cxx_intent_trigger.h | 87 + ...echapi_cxx_keyword_recognition_eventargs.h | 86 + .../speechapi_cxx_keyword_recognition_model.h | 101 + ...speechapi_cxx_keyword_recognition_result.h | 44 + .../speechapi_cxx_keyword_recognizer.h | 213 +++ ...eechapi_cxx_language_understanding_model.h | 113 ++ .../include/cxx_api/speechapi_cxx_log_level.h | 66 + .../include/cxx_api/speechapi_cxx_meeting.h | 340 ++++ .../speechapi_cxx_meeting_transcriber.h | 467 +++++ ...hapi_cxx_meeting_transcription_eventargs.h | 168 ++ ...eechapi_cxx_meeting_transcription_result.h | 96 + .../cxx_api/speechapi_cxx_memory_logger.h | 163 ++ .../cxx_api/speechapi_cxx_participant.h | 222 +++ .../speechapi_cxx_pattern_matching_entity.h | 46 + .../speechapi_cxx_pattern_matching_intent.h | 36 + .../speechapi_cxx_pattern_matching_model.h | 372 ++++ .../speechapi_cxx_phrase_list_grammar.h | 92 + ...hapi_cxx_pronunciation_assessment_config.h | 222 +++ ...hapi_cxx_pronunciation_assessment_result.h | 142 ++ .../cxx_api/speechapi_cxx_properties.h | 99 + ...eechapi_cxx_recognition_async_recognizer.h | 473 +++++ ...pi_cxx_recognition_base_async_recognizer.h | 53 + .../speechapi_cxx_recognition_eventargs.h | 68 + .../speechapi_cxx_recognition_result.h | 310 +++ .../cxx_api/speechapi_cxx_recognizer.h | 72 + .../include/cxx_api/speechapi_cxx_session.h | 86 + .../cxx_api/speechapi_cxx_session_eventargs.h | 73 + .../cxx_api/speechapi_cxx_smart_handle.h | 60 + .../speechapi_cxx_source_lang_config.h | 91 + ...speechapi_cxx_source_language_recognizer.h | 173 ++ ...eechapi_cxx_speaker_identification_model.h | 77 + ...speechapi_cxx_speaker_recognition_result.h | 236 +++ .../speechapi_cxx_speaker_recognizer.h | 142 ++ ...speechapi_cxx_speaker_verification_model.h | 71 + .../cxx_api/speechapi_cxx_speech_config.h | 491 +++++ ...eechapi_cxx_speech_recognition_eventargs.h | 169 ++ .../speechapi_cxx_speech_recognition_model.h | 108 ++ .../speechapi_cxx_speech_recognition_result.h | 45 + .../cxx_api/speechapi_cxx_speech_recognizer.h | 351 ++++ ..._cxx_speech_synthesis_bookmark_eventargs.h | 81 + ...speechapi_cxx_speech_synthesis_eventargs.h | 70 + .../speechapi_cxx_speech_synthesis_request.h | 239 +++ .../speechapi_cxx_speech_synthesis_result.h | 310 +++ ...pi_cxx_speech_synthesis_viseme_eventargs.h | 88 + ...speech_synthesis_word_boundary_eventargs.h | 120 ++ .../speechapi_cxx_speech_synthesizer.h | 793 ++++++++ .../speechapi_cxx_speech_translation_config.h | 213 +++ .../speechapi_cxx_speech_translation_model.h | 120 ++ .../cxx_api/speechapi_cxx_string_helpers.h | 137 ++ .../speechapi_cxx_synthesis_voices_result.h | 165 ++ .../speechapi_cxx_translation_eventargs.h | 235 +++ .../speechapi_cxx_translation_recognizer.h | 352 ++++ .../speechapi_cxx_translation_result.h | 175 ++ .../include/cxx_api/speechapi_cxx_user.h | 77 + .../include/cxx_api/speechapi_cxx_utils.h | 312 +++ .../cxx_api/speechapi_cxx_voice_info.h | 196 ++ .../cxx_api/speechapi_cxx_voice_profile.h | 109 ++ .../speechapi_cxx_voice_profile_client.h | 262 +++ ...hapi_cxx_voice_profile_enrollment_result.h | 242 +++ ...peechapi_cxx_voice_profile_phrase_result.h | 194 ++ .../speechapi_cxx_voice_profile_result.h | 180 ++ ...icrosoft.CognitiveServices.Speech.core.lib | Bin 0 -> 200466 bytes ...icrosoft.CognitiveServices.Speech.core.dll | Bin 0 -> 2286632 bytes ...iveServices.Speech.extension.audio.sys.dll | Bin 0 -> 128544 bytes ...gnitiveServices.Speech.extension.codec.dll | Bin 0 -> 103456 bytes ...CognitiveServices.Speech.extension.kws.dll | Bin 0 -> 311864 bytes ...itiveServices.Speech.extension.kws.ort.dll | Bin 0 -> 2076704 bytes ....CognitiveServices.Speech.extension.lu.dll | Bin 0 -> 381984 bytes .../SpeexDSP/include/speex/speex_buffer.h | 68 + .../SpeexDSP/include/speex/speex_echo.h | 170 ++ .../SpeexDSP/include/speex/speex_jitter.h | 197 ++ .../SpeexDSP/include/speex/speex_preprocess.h | 219 +++ .../SpeexDSP/include/speex/speex_resampler.h | 343 ++++ .../include/speex/speexdsp_config_types.h.in | 12 + .../SpeexDSP/include/speex/speexdsp_types.h | 126 ++ .../ThirdParty/SpeexDSP/lib/libspeexdsp-1.dll | Bin 0 -> 538177 bytes .../ThirdParty/SpeexDSP/lib/libspeexdsp.dll.a | Bin 0 -> 53342 bytes .../Source/ThirdParty/fvad/include/fvad.h | 96 + .../Source/ThirdParty/fvad/lib/fvad.lib | Bin 0 -> 39698 bytes .../ThirdParty/portaudio/include/pa_jack.h | 77 + .../portaudio/include/pa_linux_alsa.h | 107 ++ .../portaudio/include/pa_mac_core.h | 191 ++ .../ThirdParty/portaudio/include/pa_win_ds.h | 95 + .../portaudio/include/pa_win_wasapi.h | 729 +++++++ .../portaudio/include/pa_win_waveformat.h | 199 ++ .../portaudio/include/pa_win_wdmks.h | 137 ++ .../portaudio/include/pa_win_wmme.h | 185 ++ .../ThirdParty/portaudio/include/portaudio.h | 1228 ++++++++++++ .../portaudio/lib/portaudio_x64.dll | Bin 0 -> 306176 bytes .../portaudio/lib/portaudio_x64.lib | Bin 0 -> 13842 bytes 196 files changed, 30208 insertions(+), 22 deletions(-) create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Whisper/STTProcessorWhisper.cpp create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Whisper/STTWhisperProcessorConfig.cpp create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Whisper/STTProcessorWhisper.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Whisper/STTWhisperProcessorConfig.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/CMakeLists.txt create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_api_c_common.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_api_c_diagnostics.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_api_c_error.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_api_c_pal.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_debug.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_error.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_audio_config.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_audio_processing_options.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_audio_stream.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_audio_stream_format.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_auto_detect_source_lang_config.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_common.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_connection.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_conversation.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_conversation_transcription_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_conversation_translator.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_diagnostics.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_dialog_service_config.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_dialog_service_connector.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_embedded_speech_config.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_error.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_ext_audiocompression.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_factory.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_grammar.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_hybrid_speech_config.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_intent_recognizer.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_intent_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_intent_trigger.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_json.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_keyword_recognition_model.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_language_understanding_model.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_meeting.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_meeting_transcription_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_operations.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_participant.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_pattern_matching_model.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_pronunciation_assessment_config.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_property_bag.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_recognizer.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_session.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_source_lang_config.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speaker_recognition.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speech_config.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speech_recognition_model.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speech_translation_config.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speech_translation_model.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_synthesis_request.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_synthesizer.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_translation_recognizer.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_translation_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_user.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/spxdebug.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/spxerror.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/CMakeLists.txt create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/azac_api_cxx_common.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_config.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_data_stream.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_processing_options.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_stream.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_stream_format.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_auto_detect_source_lang_config.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_auto_detect_source_lang_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_class_language_model.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_common.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_connection.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_connection_eventargs.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_connection_message.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_connection_message_eventargs.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_transcriber.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_transcription_eventargs.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_transcription_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_translator.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_translator_events.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversational_language_understanding_model.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_dialog_service_config.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_dialog_service_connector.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_dialog_service_connector_eventargs.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_embedded_speech_config.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_enums.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_event_logger.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_eventargs.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_eventsignal.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_eventsignalbase.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_file_logger.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_grammar.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_grammar_list.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_grammar_phrase.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_hybrid_speech_config.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_intent_recognition_eventargs.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_intent_recognition_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_intent_recognizer.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_intent_trigger.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_keyword_recognition_eventargs.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_keyword_recognition_model.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_keyword_recognition_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_keyword_recognizer.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_language_understanding_model.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_log_level.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_meeting.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_meeting_transcriber.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_meeting_transcription_eventargs.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_meeting_transcription_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_memory_logger.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_participant.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pattern_matching_entity.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pattern_matching_intent.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pattern_matching_model.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_phrase_list_grammar.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pronunciation_assessment_config.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pronunciation_assessment_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_properties.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognition_async_recognizer.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognition_base_async_recognizer.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognition_eventargs.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognition_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognizer.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_session.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_session_eventargs.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_smart_handle.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_source_lang_config.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_source_language_recognizer.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speaker_identification_model.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speaker_recognition_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speaker_recognizer.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speaker_verification_model.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_config.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_recognition_eventargs.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_recognition_model.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_recognition_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_recognizer.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_bookmark_eventargs.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_eventargs.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_request.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_viseme_eventargs.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_word_boundary_eventargs.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesizer.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_translation_config.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_translation_model.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_string_helpers.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_synthesis_voices_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_translation_eventargs.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_translation_recognizer.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_translation_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_user.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_utils.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_info.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile_client.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile_enrollment_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile_phrase_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile_result.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/libs/Microsoft.CognitiveServices.Speech.core.lib create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/libs/Runtime/Microsoft.CognitiveServices.Speech.core.dll create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/libs/Runtime/Microsoft.CognitiveServices.Speech.extension.audio.sys.dll create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/libs/Runtime/Microsoft.CognitiveServices.Speech.extension.codec.dll create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/libs/Runtime/Microsoft.CognitiveServices.Speech.extension.kws.dll create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/libs/Runtime/Microsoft.CognitiveServices.Speech.extension.kws.ort.dll create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/libs/Runtime/Microsoft.CognitiveServices.Speech.extension.lu.dll create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/SpeexDSP/include/speex/speex_buffer.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/SpeexDSP/include/speex/speex_echo.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/SpeexDSP/include/speex/speex_jitter.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/SpeexDSP/include/speex/speex_preprocess.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/SpeexDSP/include/speex/speex_resampler.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/SpeexDSP/include/speex/speexdsp_config_types.h.in create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/SpeexDSP/include/speex/speexdsp_types.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/SpeexDSP/lib/libspeexdsp-1.dll create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/SpeexDSP/lib/libspeexdsp.dll.a create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/fvad/include/fvad.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/fvad/lib/fvad.lib create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/portaudio/include/pa_jack.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/portaudio/include/pa_linux_alsa.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/portaudio/include/pa_mac_core.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/portaudio/include/pa_win_ds.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/portaudio/include/pa_win_wasapi.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/portaudio/include/pa_win_waveformat.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/portaudio/include/pa_win_wdmks.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/portaudio/include/pa_win_wmme.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/portaudio/include/portaudio.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/portaudio/lib/portaudio_x64.dll create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/portaudio/lib/portaudio_x64.lib diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/AvatarCore_STT.Build.cs b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/AvatarCore_STT.Build.cs index f111eb3..8533c4f 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/AvatarCore_STT.Build.cs +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/AvatarCore_STT.Build.cs @@ -39,26 +39,26 @@ public class AvatarCore_STT : ModuleRules PublicIncludePaths.AddRange( new string[] { // ... add public include paths required here ... - Path.Combine(ModuleDirectory, "ThirdParty", "portaudio", "include"), + Path.Combine(ModuleDirectory, "..", "ThirdParty", "portaudio", "include"), } ); - PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "ThirdParty", "fvad", "include")); - PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "ThirdParty", "fvad", "lib", "fvad.lib")); + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "..", "ThirdParty", "fvad", "include")); + PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "..", "ThirdParty", "fvad", "lib", "fvad.lib")); - PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "ThirdParty", "SpeexDSP", "include")); - PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "ThirdParty", "SpeexDSP", "lib", "libspeexdsp.dll.a")); - PublicDelayLoadDLLs.Add(Path.Combine(ModuleDirectory, "ThirdParty", "SpeexDSP", "lib", "libspeexdsp-1.dll")); - RuntimeDependencies.Add(Path.Combine(@"$(BinaryOutputDir)", "libspeexdsp-1.dll"), Path.Combine(ModuleDirectory, "ThirdParty", "SpeexDSP", "lib", "libspeexdsp-1.dll")); + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "..", "ThirdParty", "SpeexDSP", "include")); + PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "..", "ThirdParty", "SpeexDSP", "lib", "libspeexdsp.dll.a")); + PublicDelayLoadDLLs.Add(Path.Combine(ModuleDirectory, "..", "ThirdParty", "SpeexDSP", "lib", "libspeexdsp-1.dll")); + RuntimeDependencies.Add(Path.Combine(@"$(BinaryOutputDir)", "libspeexdsp-1.dll"), Path.Combine(ModuleDirectory, "..", "ThirdParty", "SpeexDSP", "lib", "libspeexdsp-1.dll")); CppStandard = CppStandardVersion.Cpp20; - PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "ThirdParty", "AzureWrapper", "include", "c_api")); - PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "ThirdParty", "AzureWrapper", "include", "cxx_api")); - PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "ThirdParty", "AzureWrapper", "libs", "Microsoft.CognitiveServices.Speech.core.lib")); + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "..", "ThirdParty", "AzureWrapper", "include", "c_api")); + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "..", "ThirdParty", "AzureWrapper", "include", "cxx_api")); + PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "..", "ThirdParty", "AzureWrapper", "libs", "Microsoft.CognitiveServices.Speech.core.lib")); foreach (string DynamicLib in GetDynamicLibraries()) { - PublicDelayLoadDLLs.Add(Path.Combine(ModuleDirectory, "ThirdParty", "AzureWrapper", "libs", "Runtime", DynamicLib)); - RuntimeDependencies.Add(Path.Combine(@"$(BinaryOutputDir)", DynamicLib), Path.Combine(ModuleDirectory, "ThirdParty", "AzureWrapper", "libs", "Runtime", DynamicLib)); + PublicDelayLoadDLLs.Add(Path.Combine(ModuleDirectory, "..", "ThirdParty", "AzureWrapper", "libs", "Runtime", DynamicLib)); + RuntimeDependencies.Add(Path.Combine(@"$(BinaryOutputDir)", DynamicLib), Path.Combine(ModuleDirectory, "..", "ThirdParty", "AzureWrapper", "libs", "Runtime", DynamicLib)); } PrivateIncludePaths.AddRange( @@ -80,7 +80,7 @@ public class AvatarCore_STT : ModuleRules // PortAudio linking for Win64 if (Target.Platform == UnrealTargetPlatform.Win64) { - string PortAudioLibDir = Path.Combine(ModuleDirectory, "ThirdParty", "portaudio", "lib"); + string PortAudioLibDir = Path.Combine(ModuleDirectory, "..", "ThirdParty", "portaudio", "lib"); PublicAdditionalLibraries.Add(Path.Combine(PortAudioLibDir, "portaudio_x64.lib")); // Link .lib for static symbols string PortAudioDllPath = Path.Combine(PortAudioLibDir, "portaudio_x64.dll"); PublicDelayLoadDLLs.Add(PortAudioDllPath); @@ -95,6 +95,9 @@ public class AvatarCore_STT : ModuleRules "Slate", "SlateCore", "AvatarCore_AI", + "HTTP", + "Json", + "JsonUtilities", // ... add private dependencies that you statically link with here ... } ); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Azure/STTProcessorAzure.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Azure/STTProcessorAzure.cpp index 2bff435..78db7d0 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Azure/STTProcessorAzure.cpp +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Azure/STTProcessorAzure.cpp @@ -51,7 +51,7 @@ void USTTProcessorAzure::InitSTTProcessor(USTTManagerBase* BaseSTTManager, USTTB if(bDebugMode) STTManager->OnSTTLog.Broadcast(TEXT("SpeechConfig initialized successfully.")); - AzureRunnable = MakeUnique(config, audioConfig, AzureProcessorConfig->AzurePhraseList, this, true); + AzureRunnable = MakeUnique(config, audioConfig, STTManager->GetSpecialWords(), this, true); } void USTTProcessorAzure::ClearSTTProcessor() @@ -118,7 +118,7 @@ void USTTProcessorAzure::StartRecognition() StopRecognition(true); //In case there is something else running intermediateResult = ""; USTTProcessorBase::OnTranscriptionStarted(); - AzureRunnable = MakeUnique(config, audioConfig, AzureProcessorConfig->AzurePhraseList, this, false); + AzureRunnable = MakeUnique(config, audioConfig, STTManager->GetSpecialWords(), this, false); } void USTTProcessorAzure::StopRecognition(bool Forced) diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Whisper/STTProcessorWhisper.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Whisper/STTProcessorWhisper.cpp new file mode 100644 index 0000000..cbc1069 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Whisper/STTProcessorWhisper.cpp @@ -0,0 +1,438 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Processor/Whisper/STTProcessorWhisper.h" +#include "STTManagerBase.h" +#include "HttpModule.h" +#include "Interfaces/IHttpRequest.h" +#include "Interfaces/IHttpResponse.h" +#include "Serialization/JsonSerializer.h" +#include "Dom/JsonObject.h" + +namespace +{ + static void AppendStringToBody(TArray& Body, const FString& Str) + { + FTCHARToUTF8 Converter(*Str); + Body.Append(reinterpret_cast(Converter.Get()), Converter.Length()); + } + + static const FString TranscribeModelEnumToString(EOpenAITranscriptionModel Model) + { + switch (Model) + { + case EOpenAITranscriptionModel::Whisper1: + return TEXT("whisper-1"); + break; + + case EOpenAITranscriptionModel::TranscribeMini4o: + return TEXT("gpt-4o-mini-transcribe"); + break; + + case EOpenAITranscriptionModel::Transcribe4o: + return TEXT("gpt-4o-transcribe"); + break; + + default: + return TEXT(""); + break; + } + } +} + +void USTTProcessorWhisper::InitSTTProcessor(USTTManagerBase* BaseSTTManager, USTTBaseProcessorConfig* InProcessorConfig, bool InDebugMode) +{ + USTTProcessorBase::InitSTTProcessor(BaseSTTManager, InProcessorConfig, InDebugMode); + WhisperProcessorConfig = Cast(InProcessorConfig); + if(!WhisperProcessorConfig) + { + if (IsValid(STTManager)) + STTManager->OnSTTError.Broadcast(TEXT("Whisper Processor Config is invalid.")); + return; + } + + if (WhisperProcessorConfig->OpenAI_API_Key.IsEmpty()) { + if (IsValid(STTManager)) + STTManager->OnSTTError.Broadcast(TEXT("OpenAI API Key not set. Needs to be done before initializing modules.")); + return; + } + + NormalizeWhisperURL(); + + if (IsValid(STTManager)) + { + STTManager->OnSpeechStateChanged.AddUniqueDynamic(this, &USTTProcessorWhisper::OnSpeechStateChanged); + } + + PerformHealthCheck(); +} + +void USTTProcessorWhisper::ClearSTTProcessor() +{ + USTTProcessorBase::ClearSTTProcessor(); + BufferedPCMData.Empty(); + bHasBufferedAudioInformation = false; + + for (TSharedPtr& Request : ActiveRequests) + { + if (Request.IsValid()) + { + Request->OnProcessRequestComplete().Unbind(); + Request->CancelRequest(); + } + } + ActiveRequests.Empty(); +} + +void USTTProcessorWhisper::DestroySTTProcessor() +{ + ClearSTTProcessor(); + STTManager = nullptr; +} + +void USTTProcessorWhisper::OnChunkReceived(TArray PCMData, FAudioInformation AudioInformation) +{ + if (CurrentTalkingState != ESTTTalkingState::TALKING) + return; + + if (PCMData.Num() == 0) + return; + + if (!bHasBufferedAudioInformation) + { + BufferedAudioInformation = AudioInformation; + bHasBufferedAudioInformation = true; + } + else if (BufferedAudioInformation.SampleRate != AudioInformation.SampleRate || + BufferedAudioInformation.NumChannels != AudioInformation.NumChannels) + { + BufferedPCMData.Empty(); + BufferedAudioInformation = AudioInformation; + } + + const int64 BytesPerSample = sizeof(int16); + const int64 CurrentBytes = static_cast(BufferedPCMData.Num()) * BytesPerSample; + const int64 NewBytes = static_cast(PCMData.Num()) * BytesPerSample; + const int64 MaxUploadBytes = static_cast(25) * 1024 * 1024; + const int64 MaxAudioBytes = MaxUploadBytes - 1024; + + if (CurrentBytes + NewBytes > MaxAudioBytes) + { + int64 ExcessBytes = CurrentBytes + NewBytes - MaxAudioBytes; + int64 SamplesToRemove = (ExcessBytes + BytesPerSample - 1) / BytesPerSample; + + if (SamplesToRemove >= BufferedPCMData.Num()) + { + BufferedPCMData.Empty(); + if (NewBytes > MaxAudioBytes) + { + int64 NewSamplesAllowed = MaxAudioBytes / BytesPerSample; + if (NewSamplesAllowed > 0 && NewSamplesAllowed < PCMData.Num()) + { + int32 StartIndex = PCMData.Num() - static_cast(NewSamplesAllowed); + BufferedPCMData.Append(&PCMData[StartIndex], static_cast(NewSamplesAllowed)); + } + } + else + { + BufferedPCMData.Append(PCMData); + } + } + else + { + BufferedPCMData.RemoveAt(0, static_cast(SamplesToRemove), EAllowShrinking::No); + BufferedPCMData.Append(PCMData); + } + } + else + { + BufferedPCMData.Append(PCMData); + } +} + +void USTTProcessorWhisper::OnSpeechStateChanged(ESTTTalkingState TalkingState) +{ + CurrentTalkingState = TalkingState; + + if (TalkingState == ESTTTalkingState::BLOCKED) + { + ClearSTTProcessor(); + return; + } + + if (TalkingState == ESTTTalkingState::SILENCE || TalkingState == ESTTTalkingState::TRANSCRIBING) + { + StartTranscriptionFromBuffer(); + } +} + +void USTTProcessorWhisper::StartTranscriptionFromBuffer() +{ + + if (BufferedPCMData.Num() == 0 || !bHasBufferedAudioInformation) + return; + + TArray PCMDataCopy = BufferedPCMData; + FAudioInformation AudioInfoCopy = BufferedAudioInformation; + BufferedPCMData.Empty(); + bHasBufferedAudioInformation = false; + + // Require at least x seconds of audio before sending to Whisper + if (AudioInfoCopy.SampleRate > 0 && AudioInfoCopy.NumChannels > 0) + { + const float Frames = static_cast(PCMDataCopy.Num()) / static_cast(AudioInfoCopy.NumChannels); + const float DurationSeconds = Frames / static_cast(AudioInfoCopy.SampleRate); + if (DurationSeconds < WhisperProcessorConfig->MinDuration) + { + return; + } + } + + TArray WavData; + if (!BuildWavFromPCM(PCMDataCopy, AudioInfoCopy, WavData)) + { + if (IsValid(STTManager)) + STTManager->OnSTTError.Broadcast(TEXT("Failed to build WAV data for Whisper transcription.")); + return; + } + + const int64 MaxUploadBytes = static_cast(25) * 1024 * 1024; + if (WavData.Num() > MaxUploadBytes) + { + if (IsValid(STTManager)) + STTManager->OnSTTError.Broadcast(TEXT("Whisper audio size exceeds 25MB limit. Transcription aborted.")); + return; + } + + SendWhisperRequest(MoveTemp(WavData)); +} + +bool USTTProcessorWhisper::BuildWavFromPCM(const TArray& PCMData, const FAudioInformation& AudioInformation, TArray& OutWavData) const +{ + if (PCMData.Num() == 0) + return false; + + if (AudioInformation.SampleRate <= 0 || AudioInformation.NumChannels <= 0) + return false; + + const uint32 BitsPerSample = 16; + const uint32 BytesPerSample = BitsPerSample / 8; + const uint32 NumSamples = static_cast(PCMData.Num()); + const uint32 ByteRate = static_cast(AudioInformation.SampleRate) * static_cast(AudioInformation.NumChannels) * BytesPerSample; + const uint32 BlockAlign = static_cast(AudioInformation.NumChannels) * BytesPerSample; + const uint32 Subchunk1Size = 16; + const uint32 AudioFormat = 1; + const uint32 Subchunk2Size = NumSamples * BytesPerSample; + const uint32 ChunkSize = 4 + (8 + Subchunk1Size) + (8 + Subchunk2Size); + + OutWavData.Reset(); + OutWavData.Reserve(ChunkSize + 8); + + auto AppendLittleEndian = [&OutWavData](uint32 Value, int32 ByteCount) + { + for (int32 i = 0; i < ByteCount; i++) + { + OutWavData.Add(static_cast((Value >> (i * 8)) & 0xFF)); + } + }; + + OutWavData.Append(reinterpret_cast("RIFF"), 4); + AppendLittleEndian(ChunkSize, 4); + OutWavData.Append(reinterpret_cast("WAVE"), 4); + + OutWavData.Append(reinterpret_cast("fmt "), 4); + AppendLittleEndian(Subchunk1Size, 4); + AppendLittleEndian(AudioFormat, 2); + AppendLittleEndian(static_cast(AudioInformation.NumChannels), 2); + AppendLittleEndian(static_cast(AudioInformation.SampleRate), 4); + AppendLittleEndian(ByteRate, 4); + AppendLittleEndian(BlockAlign, 2); + AppendLittleEndian(BitsPerSample, 2); + + OutWavData.Append(reinterpret_cast("data"), 4); + AppendLittleEndian(Subchunk2Size, 4); + + const uint8* PCMDataPtr = reinterpret_cast(PCMData.GetData()); + OutWavData.Append(PCMDataPtr, Subchunk2Size); + + return true; +} + +void USTTProcessorWhisper::BuildMultipartBody(const TArray& WavData, const FString& Boundary, TArray& OutBody, const FString& Prompt) const +{ + OutBody.Reset(); + + FString BoundaryLine = FString::Printf(TEXT("--%s\r\n"), *Boundary); + AppendStringToBody(OutBody, BoundaryLine); + AppendStringToBody(OutBody, TEXT("Content-Disposition: form-data; name=\"model\"\r\n\r\n")); + AppendStringToBody(OutBody, TranscribeModelEnumToString(WhisperProcessorConfig->Model) + TEXT("\r\n")); + + if (!Prompt.IsEmpty()) + { + BoundaryLine = FString::Printf(TEXT("--%s\r\n"), *Boundary); + AppendStringToBody(OutBody, BoundaryLine); + AppendStringToBody(OutBody, TEXT("Content-Disposition: form-data; name=\"prompt\"\r\n\r\n")); + AppendStringToBody(OutBody, Prompt + TEXT("\r\n")); + } + + BoundaryLine = FString::Printf(TEXT("--%s\r\n"), *Boundary); + AppendStringToBody(OutBody, BoundaryLine); + AppendStringToBody(OutBody, TEXT("Content-Disposition: form-data; name=\"file\"; filename=\"audio.wav\"\r\n")); + AppendStringToBody(OutBody, TEXT("Content-Type: audio/wav\r\n\r\n")); + OutBody.Append(WavData); + AppendStringToBody(OutBody, TEXT("\r\n")); + + BoundaryLine = FString::Printf(TEXT("--%s--\r\n"), *Boundary); + AppendStringToBody(OutBody, BoundaryLine); +} + +void USTTProcessorWhisper::NormalizeWhisperURL() +{ + if (!WhisperProcessorConfig) + return; + + NormalizedWhisperURL = WhisperProcessorConfig->WhisperURL; + if (!NormalizedWhisperURL.StartsWith(TEXT("http://")) && !NormalizedWhisperURL.StartsWith(TEXT("https://"))) + { + NormalizedWhisperURL = FString::Printf(TEXT("https://%s"), *NormalizedWhisperURL); + } +} + +void USTTProcessorWhisper::PerformHealthCheck() +{ + if (!WhisperProcessorConfig) + return; + + if (NormalizedWhisperURL.IsEmpty()) + NormalizeWhisperURL(); + + FHttpModule& HttpModule = FHttpModule::Get(); + TSharedRef Request = HttpModule.CreateRequest(); + Request->SetURL(NormalizedWhisperURL); + Request->SetVerb(TEXT("GET")); + FString AuthHeader = FString::Printf(TEXT("Bearer %s"), *WhisperProcessorConfig->OpenAI_API_Key); + Request->SetHeader(TEXT("Authorization"), AuthHeader); + Request->OnProcessRequestComplete().BindLambda([ + WeakManager = TWeakObjectPtr(STTManager)](FHttpRequestPtr Req, FHttpResponsePtr Resp, bool bWasSuccessful) + { + if (!WeakManager.IsValid()) + return; + + USTTManagerBase* Manager = WeakManager.Get(); + if (!bWasSuccessful || !Resp.IsValid()) + { + Manager->OnSTTError.Broadcast(TEXT("Whisper initialization check failed: URL not reachable.")); + return; + } + + int32 StatusCode = Resp->GetResponseCode(); + if (StatusCode == 401) + { + Manager->OnSTTError.Broadcast(TEXT("Whisper initialization check failed: API key invalid (401).")); + } + }); + + Request->ProcessRequest(); +} + +void USTTProcessorWhisper::SendWhisperRequest(TArray&& WavData) +{ + if (!WhisperProcessorConfig) + return; + + if (NormalizedWhisperURL.IsEmpty()) + NormalizeWhisperURL(); + + FHttpModule& HttpModule = FHttpModule::Get(); + TSharedRef Request = HttpModule.CreateRequest(); + Request->SetURL(NormalizedWhisperURL); + Request->SetVerb(TEXT("POST")); + + FString AuthHeader = FString::Printf(TEXT("Bearer %s"), *WhisperProcessorConfig->OpenAI_API_Key); + Request->SetHeader(TEXT("Authorization"), AuthHeader); + Request->SetHeader(TEXT("Accept"), TEXT("application/json")); + + FString Boundary = TEXT("----AvatarCoreSTTWhisperBoundary"); + FString ContentType = FString::Printf(TEXT("multipart/form-data; boundary=%s"), *Boundary); + Request->SetHeader(TEXT("Content-Type"), ContentType); + + FString Prompt; + if (IsValid(STTManager)) + { + Prompt = STTManager->GetSpecialWordsAsString(); + } + + TArray Body; + BuildMultipartBody(WavData, Boundary, Body, Prompt); + Request->SetContent(Body); + + OnTranscriptionStarted(); + int32 TranscriptionId = TranscriptionCounter; + + TWeakObjectPtr WeakThis(this); + TSharedPtr RequestPtr = Request; + Request->OnProcessRequestComplete().BindLambda([ + WeakThis, + RequestPtr, + TranscriptionId](FHttpRequestPtr Req, FHttpResponsePtr Resp, bool bWasSuccessful) + { + if (!WeakThis.IsValid()) + return; + + USTTProcessorWhisper* Self = WeakThis.Get(); + Self->ActiveRequests.Remove(RequestPtr); + + if (!bWasSuccessful || !Resp.IsValid()) + { + if (IsValid(Self->STTManager)) + Self->STTManager->OnSTTError.Broadcast(TEXT("Whisper request failed or no response received.")); + Self->bTranscriptionRunning = false; + return; + } + + int32 StatusCode = Resp->GetResponseCode(); + if (StatusCode == 401) + { + if (IsValid(Self->STTManager)) + Self->STTManager->OnSTTError.Broadcast(TEXT("Whisper request unauthorized (401). Please check API key.")); + Self->bTranscriptionRunning = false; + return; + } + else if (StatusCode < 200 || StatusCode >= 300) + { + FString ErrorBody = Resp->GetContentAsString(); + if (IsValid(Self->STTManager)) + { + FString ErrorMessage = FString::Printf(TEXT("Whisper request failed with HTTP %d: %s"), StatusCode, *ErrorBody); + Self->STTManager->OnSTTError.Broadcast(ErrorMessage); + } + Self->bTranscriptionRunning = false; + return; + } + + FString JsonString = Resp->GetContentAsString(); + TSharedPtr RootObject; + TSharedRef> Reader = TJsonReaderFactory<>::Create(JsonString); + if (!FJsonSerializer::Deserialize(Reader, RootObject) || !RootObject.IsValid()) + { + if (IsValid(Self->STTManager)) + Self->STTManager->OnSTTError.Broadcast(TEXT("Failed to parse Whisper JSON response.")); + Self->bTranscriptionRunning = false; + return; + } + + FString Text; + if (!RootObject->TryGetStringField(TEXT("text"), Text)) + { + if (IsValid(Self->STTManager)) + Self->STTManager->OnSTTError.Broadcast(TEXT("Whisper response JSON does not contain 'text' field.")); + Self->bTranscriptionRunning = false; + return; + } + + Self->OnTranscriptionResult(TranscriptionId, Text); + }); + + ActiveRequests.Add(RequestPtr); + Request->ProcessRequest(); +} \ No newline at end of file diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Whisper/STTWhisperProcessorConfig.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Whisper/STTWhisperProcessorConfig.cpp new file mode 100644 index 0000000..19a81e2 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Whisper/STTWhisperProcessorConfig.cpp @@ -0,0 +1,10 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Processor/Whisper/STTWhisperProcessorConfig.h" +#include "Processor/Whisper/STTProcessorWhisper.h" + +USTTWhisperProcessorConfig::USTTWhisperProcessorConfig(const FObjectInitializer& ObjectInitializer) +{ + STTProcessorClass = USTTProcessorWhisper::StaticClass(); +} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/STTManagerBase.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/STTManagerBase.cpp index d761513..50a1c19 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/STTManagerBase.cpp +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/STTManagerBase.cpp @@ -182,15 +182,24 @@ void USTTManagerBase::RemoveSpecialWords(const TArray WordsToRemove) TArray USTTManagerBase::GetSpecialWords() { - return SpecialWords; + TArray CombinedSpecialWords; + CombinedSpecialWords.Append(ProcessorConfig->BaseSettings.STTSpecialWords); + CombinedSpecialWords.Append(SpecialWords); + return CombinedSpecialWords; } FString USTTManagerBase::GetSpecialWordsAsString() { FString SpecialWordsString; - for (int i = 0; i < SpecialWords.Num(); i++) { - SpecialWordsString.Append(SpecialWords[i]); - if (i < SpecialWords.Num() - 1) + + //Combine both the special words from STTSettings globally as also the on runtime special words (Kira, Aki etc.) + TArray CombinedSpecialWords; + CombinedSpecialWords.Append(ProcessorConfig->BaseSettings.STTSpecialWords); + CombinedSpecialWords.Append(SpecialWords); + + for (int i = 0; i < CombinedSpecialWords.Num(); i++) { + SpecialWordsString.Append(CombinedSpecialWords[i]); + if (i < CombinedSpecialWords.Num() - 1) SpecialWordsString.Append(TEXT(", ")); } return SpecialWordsString; diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Azure/STTAzureProcessorConfig.h b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Azure/STTAzureProcessorConfig.h index fa66143..7e69364 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Azure/STTAzureProcessorConfig.h +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Azure/STTAzureProcessorConfig.h @@ -37,8 +37,6 @@ public: USTTAzureProcessorConfig(const FObjectInitializer& ObjectInitializer); - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|Azure", meta = (ExposeOnSpawn = "true")) - TArray AzurePhraseList = { TEXT("b.ReX"), TEXT("Bruce B.") }; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|Azure", meta = (ExposeOnSpawn = "true")) FString AzureAPIKey = ""; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|Azure", meta = (ExposeOnSpawn = "true")) diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/STTProcessorBase.h b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/STTProcessorBase.h index d0a26f5..a802b16 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/STTProcessorBase.h +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/STTProcessorBase.h @@ -24,7 +24,7 @@ public: virtual void InitSTTProcessor(USTTManagerBase* BaseSTTManager, USTTBaseProcessorConfig* InPreprocessorConfig, bool InDebugMode = false) { STTManager = BaseSTTManager; PreprocessorConfig = InPreprocessorConfig; bDebugMode = InDebugMode; }; UFUNCTION() - void ClearSTTProcessor(); + virtual void ClearSTTProcessor(); UFUNCTION() virtual void DestroySTTProcessor() {}; diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Whisper/STTProcessorWhisper.h b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Whisper/STTProcessorWhisper.h new file mode 100644 index 0000000..2010520 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Whisper/STTProcessorWhisper.h @@ -0,0 +1,47 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Processor/STTProcessorBase.h" +#include "Processor/Whisper/STTWhisperProcessorConfig.h" +#include "STTProcessorWhisper.generated.h" + +/** + * + */ +UCLASS() +class USTTProcessorWhisper : public USTTProcessorBase +{ + GENERATED_BODY() + +public: + + void InitSTTProcessor(USTTManagerBase* BaseSTTManager, USTTBaseProcessorConfig* InProcessorConfig, bool InDebugMode = false) override; + virtual void ClearSTTProcessor() override; + virtual void DestroySTTProcessor() override; + + virtual void OnChunkReceived(TArray PCMData, FAudioInformation AudioInformation) override; + + UFUNCTION() + void OnSpeechStateChanged(ESTTTalkingState TalkingState); + +private: + + void StartTranscriptionFromBuffer(); + bool BuildWavFromPCM(const TArray& PCMData, const FAudioInformation& AudioInformation, TArray& OutWavData) const; + void BuildMultipartBody(const TArray& WavData, const FString& Boundary, TArray& OutBody, const FString& Prompt) const; + void NormalizeWhisperURL(); + void PerformHealthCheck(); + void SendWhisperRequest(TArray&& WavData); + +private: + + USTTWhisperProcessorConfig* WhisperProcessorConfig = nullptr; + FString NormalizedWhisperURL; + TArray BufferedPCMData; + FAudioInformation BufferedAudioInformation; + bool bHasBufferedAudioInformation = false; + TArray> ActiveRequests; + ESTTTalkingState CurrentTalkingState = ESTTTalkingState::SILENCE; +}; diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Whisper/STTWhisperProcessorConfig.h b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Whisper/STTWhisperProcessorConfig.h new file mode 100644 index 0000000..dcd6948 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Whisper/STTWhisperProcessorConfig.h @@ -0,0 +1,38 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Processor/STTBaseProcessorConfig.h" +#include "STTWhisperProcessorConfig.generated.h" + +UENUM(BlueprintType) +enum class EOpenAITranscriptionModel : uint8 +{ + Whisper1 UMETA(DisplayName = "Whisper-1"), + TranscribeMini4o UMETA(DisplayName = "4o Transcribe Mini"), + Transcribe4o UMETA(DisplayName = "4o Transcribe") +}; + +/** + * + */ +UCLASS() +class USTTWhisperProcessorConfig : public USTTBaseProcessorConfig +{ + GENERATED_BODY() + +public: + + USTTWhisperProcessorConfig(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|Whisper", meta = (ExposeOnSpawn = "true")) + FString OpenAI_API_Key = ""; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|Whisper", meta = (ExposeOnSpawn = "true")) + FString WhisperURL = "api.openai.com/v1/audio/transcriptions"; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|Whisper", meta = (ExposeOnSpawn = "true")) + EOpenAITranscriptionModel Model = EOpenAITranscriptionModel::Transcribe4o; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|Whisper", meta = (ExposeOnSpawn = "true")) + float MinDuration = 0.75f; + +}; diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTStructs.h b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTStructs.h index 5e117c1..7bf75a2 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTStructs.h +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTStructs.h @@ -123,4 +123,7 @@ struct FSTTBaseSettings FSpeexDSPSettings SpeexDSPSettings; UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "Transcriptions to always change to another word.", Category = "STT|Base")) TArray STTReplacements; + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "Special words that the transcription service needs to know (e.g. b.ReX or Bruce-B).", Category = "STT|Base")) + TArray STTSpecialWords; + }; \ No newline at end of file diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/CMakeLists.txt b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/CMakeLists.txt new file mode 100644 index 0000000..725b6bc --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.19) + +project(c_headers) + +set(SRC_DIR "${PROJECT_SOURCE_DIR}") +add_library(${PROJECT_NAME} INTERFACE ${SPEECH_C_API_HEADERS}) +target_include_directories(${PROJECT_NAME} INTERFACE ${PROJECT_SOURCE_DIR}) +set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER api) diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_api_c_common.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_api_c_common.h new file mode 100644 index 0000000..a1e1571 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_api_c_common.h @@ -0,0 +1,81 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/azai/license202106 for the full license information. +// + +#pragma once + +// TODO: TFS#3671215 - Vision: C/C++ azac_api* files are in shared include directory, speech and vision share + +#include +#include + +#ifdef __cplusplus +#define AZAC_EXTERN_C extern "C" +#else +#define AZAC_EXTERN_C +#endif + +#ifdef _WIN32 +#define AZAC_DLL_EXPORT __declspec(dllexport) +#define AZAC_DLL_IMPORT __declspec(dllimport) +#define AZAC_API_NOTHROW __declspec(nothrow) +#define AZAC_API_RESULTTYPE AZACHR +#define AZAC_API_CALLTYPE __stdcall +#define AZAC_API_VCALLTYPE __cdecl +#else +#define AZAC_DLL_EXPORT __attribute__ ((__visibility__("default"))) +#define AZAC_DLL_IMPORT +#define AZAC_API_NOTHROW __attribute__((nothrow)) +#define AZAC_API_RESULTTYPE AZACHR +#define AZAC_API_CALLTYPE +#define AZAC_API_VCALLTYPE __attribute__((cdecl)) +#endif + +#ifdef AZAC_CONFIG_EXPORTAPIS +#define AZAC_API_EXPORT AZAC_DLL_EXPORT +#endif +#ifdef AZAC_CONFIG_IMPORTAPIS +#define AZAC_API_EXPORT AZAC_DLL_IMPORT +#endif +#ifdef AZAC_CONFIG_STATIC_LINK_APIS +#define AZAC_API_EXPORT +#endif +#ifndef AZAC_API_EXPORT +#define AZAC_API_EXPORT AZAC_DLL_IMPORT +#endif + +#define AZAC_API AZAC_EXTERN_C AZAC_API_EXPORT AZAC_API_RESULTTYPE AZAC_API_NOTHROW AZAC_API_CALLTYPE +#define AZAC_API_(type) AZAC_EXTERN_C AZAC_API_EXPORT type AZAC_API_NOTHROW AZAC_API_CALLTYPE +#define AZAC_API__(type) AZAC_EXTERN_C AZAC_API_EXPORT AZAC_API_NOTHROW type AZAC_API_CALLTYPE +#define AZAC_APIV AZAC_EXTERN_C AZAC_API_EXPORT AZAC_API_NOTHROW AZAC_API_RESULTTYPE AZAC_API_VCALLTYPE +#define AZAC_APIV_(type) AZAC_EXTERN_C AZAC_API_EXPORT AZAC_API_NOTHROW type AZAC_API_VCALLTYPE +#define AZAC_API_PRIVATE AZAC_EXTERN_C AZAC_API_RESULTTYPE AZAC_API_NOTHROW AZAC_API_CALLTYPE +#define AZAC_API_PRIVATE_(type) AZAC_EXTERN_C type AZAC_API_NOTHROW AZAC_API_CALLTYPE + +struct _azac_empty {}; +typedef struct _azac_empty* _azachandle; +typedef _azachandle AZAC_HANDLE; + +#define AZAC_HANDLE_INVALID ((AZAC_HANDLE)-1) +#define AZAC_HANDLE_RESERVED1 ((AZAC_HANDLE)+1) + +#ifndef AZAC_SUPPRESS_DIAGNOSTICS_INCLUDE_FROM_COMMON +#define AZAC_SUPPRESS_COMMON_INCLUDE_FROM_DIAGNOSTICS +#include +#undef AZAC_SUPPRESS_COMMON_INCLUDE_FROM_DIAGNOSTICS +#endif + +#ifndef AZAC_SUPPRESS_ERROR_INCLUDE_FROM_COMMON +#define AZAC_SUPPRESS_COMMON_INCLUDE_FROM_ERROR +#include +#undef AZAC_SUPPRESS_COMMON_INCLUDE_FROM_ERROR +#endif + +#ifndef AZAC_SUPPRESS_DEBUG_INCLUDE_FROM_COMMON +#define AZAC_SUPPRESS_COMMON_INCLUDE_FROM_DEBUG +#include +#undef AZAC_SUPPRESS_COMMON_INCLUDE_FROM_DEBUG +#endif + +#define AZACPROPERTYBAGHANDLE AZAC_HANDLE diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_api_c_diagnostics.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_api_c_diagnostics.h new file mode 100644 index 0000000..1059860 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_api_c_diagnostics.h @@ -0,0 +1,80 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/azai/license202106 for the full license information. +// + +#pragma once + +// TODO: TFS#3671215 - Vision: C/C++ azac_api* files are in shared include directory, speech and vision share + +#ifndef AZAC_SUPPRESS_COMMON_INCLUDE_FROM_DIAGNOSTICS +#define AZAC_SUPPRESS_DIAGNOSTICS_INCLUDE_FROM_COMMON +#include +#undef AZAC_SUPPRESS_DIAGNOSTICS_INCLUDE_FROM_COMMON +#endif + +#include +#include + +// +// APIs to manage logging to file +// +AZAC_API diagnostics_log_start_logging(AZAC_HANDLE hpropbag, void* reserved); +AZAC_API diagnostics_log_apply_properties(AZAC_HANDLE hpropbag, void* reserved); +AZAC_API diagnostics_log_stop_logging(); + +// +// APIs to manage logging events +// +typedef void(*DIAGNOSTICS_CALLBACK_FUNC)(const char *logLine); +AZAC_API diagnostics_logmessage_set_callback(DIAGNOSTICS_CALLBACK_FUNC callback); +AZAC_API diagnostics_logmessage_set_filters(const char* filters); + +// +// APIs to managed eventSource events +// +typedef void(*DIAGNOSTICS_EVENTSOURCE_CALLBACK_FUNC)(const char *logLine, const int level); +AZAC_API diagnostics_eventsource_logmessage_set_callback(DIAGNOSTICS_EVENTSOURCE_CALLBACK_FUNC callback); +AZAC_API diagnostics_eventsource_logmessage_set_filters(const char* filters); + +// +// APIs to manage logging to memory +// +AZAC_API_(void) diagnostics_log_memory_start_logging(); +AZAC_API_(void) diagnostics_log_memory_stop_logging(); +AZAC_API_(void) diagnostics_log_memory_set_filters(const char* filters); + +// The binding layers use these to implement a dump to vector of strings or an output stream +AZAC_API_(size_t) diagnostics_log_memory_get_line_num_oldest(); +AZAC_API_(size_t) diagnostics_log_memory_get_line_num_newest(); +AZAC_API__(const char*) diagnostics_log_memory_get_line(size_t lineNum); + +// Dump to file, std out or std err with optional prefix string +AZAC_API diagnostics_log_memory_dump_to_stderr(); // This calls diagnostics_log_memory_dump(nullptr, nullptr, false, true) +AZAC_API diagnostics_log_memory_dump(const char* filename, const char* linePrefix, bool emitToStdOut, bool emitToStdErr); +AZAC_API diagnostics_log_memory_dump_on_exit(const char* filename, const char* linePrefix, bool emitToStdOut, bool emitToStdErr); + +// +// APIs to manage logging to the console +// +AZAC_API_(void) diagnostics_log_console_start_logging(bool logToStderr); +AZAC_API_(void) diagnostics_log_console_stop_logging(); +AZAC_API_(void) diagnostics_log_console_set_filters(const char* filters); + +// +// APIs to log a string +// +AZAC_API_(void) diagnostics_log_format_message(char* buffer, size_t bufferSize, int level, const char* pszTitle, const char* fileName, const int lineNumber, const char* pszFormat, va_list argptr); +AZAC_API_(void) diagnostics_log_trace_string(int level, const char* pszTitle, const char* fileName, const int lineNumber, const char* psz); +AZAC_API_(void) diagnostics_log_trace_message(int level, const char* pszTitle, const char* fileName, const int lineNumber, const char* pszFormat, ...); +AZAC_API_(void) diagnostics_log_trace_message2(int level, const char* pszTitle, const char* fileName, const int lineNumber, const char* pszFormat, va_list argptr); + +AZAC_API_(void) diagnostics_set_log_level(const char * logger, const char * level); +AZAC_API_(bool) diagnostics_is_log_level_enabled(int level); + +// +// Memory tracking API's +// +AZAC_API_(size_t) diagnostics_get_handle_count(); +AZAC_API__(const char*) diagnostics_get_handle_info(); +AZAC_API diagnostics_free_string(const char* value); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_api_c_error.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_api_c_error.h new file mode 100644 index 0000000..e0653d7 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_api_c_error.h @@ -0,0 +1,24 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/azai/vision/license for the full license information. +// + +#pragma once + +// TODO: TFS#3671215 - Vision: C/C++ azac_api* files are in shared include directory, speech and vision share + +#ifndef AZAC_SUPPRESS_COMMON_INCLUDE_FROM_ERROR +#define AZAC_SUPPRESS_ERROR_INCLUDE_FROM_COMMON +#include +#undef AZAC_SUPPRESS_ERROR_INCLUDE_FROM_COMMON +#endif + +typedef const char * const_char_ptr; + +AZAC_API_(const_char_ptr) error_get_message(AZAC_HANDLE errorHandle); + +AZAC_API_(const_char_ptr) error_get_call_stack(AZAC_HANDLE errorHandle); + +AZAC_API error_get_error_code(AZAC_HANDLE errorHandle); + +AZAC_API error_release(AZAC_HANDLE errorHandle); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_api_c_pal.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_api_c_pal.h new file mode 100644 index 0000000..b67fd3a --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_api_c_pal.h @@ -0,0 +1,13 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/azai/license202106 for the full license information. +// + +#pragma once + +// TODO: TFS#3671215 - Vision: C/C++ azac_api* files are in shared include directory, speech and vision share + +#include "azac_api_c_common.h" + +AZAC_API_(size_t) pal_wstring_to_string(char * dst, const wchar_t * src, size_t dstSize); +AZAC_API_(size_t) pal_string_to_wstring(wchar_t * dst, const char * src, size_t dstSize); \ No newline at end of file diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_debug.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_debug.h new file mode 100644 index 0000000..c5ff05f --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_debug.h @@ -0,0 +1,845 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/azai/license202106 for the full license information. +// + +#pragma once +#include +#include + +// TODO: TFS#3671215 - Vision: C/C++ azac_api* files are in shared include directory, speech and vision share + +#ifndef _MSC_VER +// macros in this header generate a bunch of +// "ISO C++11 requires at least one argument for the "..." in a variadic macro" errors. +// system_header pragma is the only mechanism that helps to suppress them. +// https://stackoverflow.com/questions/35587137/how-to-suppress-gcc-variadic-macro-argument-warning-for-zero-arguments-for-a-par +// TODO: try to make macros standard-compliant. +#pragma GCC system_header +#endif + +#ifndef __cplusplus +#define static_assert _Static_assert +#endif + +#define UNUSED(x) (void)(x) + +//------------------------------------------------------- +// Re-enabled ability to compile out all macros... +// However, currently still need to keep all macros until +// final review of all macros is complete. +//------------------------------------------------------- + +#define AZAC_CONFIG_TRACE_INCLUDE_DBG_WITH_ALL 1 + +#ifdef AZAC_CONFIG_TRACE_INCLUDE_DBG_WITH_ALL +#if defined(AZAC_CONFIG_TRACE_ALL) && !defined(AZAC_CONFIG_DBG_TRACE_ALL) && (!defined(DEBUG) || !defined(_DEBUG)) +#define AZAC_CONFIG_DBG_TRACE_ALL 1 +#endif +#endif + +//------------------------------------------------------- +// AZAC_ and AZAC_DBG_ macro configuration +//------------------------------------------------------- + +#ifdef AZAC_CONFIG_DBG_TRACE_ALL +#define AZAC_CONFIG_DBG_TRACE_VERBOSE 1 +#define AZAC_CONFIG_DBG_TRACE_INFO 1 +#define AZAC_CONFIG_DBG_TRACE_WARNING 1 +#define AZAC_CONFIG_DBG_TRACE_ERROR 1 +#define AZAC_CONFIG_DBG_TRACE_FUNCTION 1 +#define AZAC_CONFIG_DBG_TRACE_SCOPE 1 +#define AZAC_CONFIG_DBG_TRACE_ASSERT 1 +#define AZAC_CONFIG_DBG_TRACE_VERIFY 1 +#ifndef AZAC_CONFIG_TRACE_ALL +#define AZAC_CONFIG_TRACE_ALL 1 +#endif +#endif + +#ifdef AZAC_CONFIG_TRACE_ALL +#define AZAC_CONFIG_TRACE_VERBOSE 1 +#define AZAC_CONFIG_TRACE_INFO 1 +#define AZAC_CONFIG_TRACE_WARNING 1 +#define AZAC_CONFIG_TRACE_ERROR 1 +#define AZAC_CONFIG_TRACE_FUNCTION 1 +#define AZAC_CONFIG_TRACE_SCOPE 1 +#define AZAC_CONFIG_TRACE_THROW_ON_FAIL 1 +#define AZAC_CONFIG_TRACE_REPORT_ON_FAIL 1 +#define AZAC_CONFIG_TRACE_RETURN_ON_FAIL 1 +#define AZAC_CONFIG_TRACE_EXITFN_ON_FAIL 1 +#endif + +//----------------------------------------------------------- +// AZAC_TRACE macro common implementations +//----------------------------------------------------------- + +#define __AZAC_TRACE_LEVEL_INFO 0x08 // Trace_Info +#define __AZAC_TRACE_LEVEL_WARNING 0x04 // Trace_Warning +#define __AZAC_TRACE_LEVEL_ERROR 0x02 // Trace_Error +#define __AZAC_TRACE_LEVEL_VERBOSE 0x10 // Trace_Verbose + +#ifndef __AZAC_DO_TRACE_IMPL +#ifdef __cplusplus +#include +#include +#include +#include +inline void __azac_do_trace_message(int level, const char* pszTitle, const char* fileName, const int lineNumber, const char* pszFormat, ...) throw() +{ + UNUSED(level); + + bool logToConsole = false; +#if defined(DEBUG) || defined(_DEBUG) + logToConsole = true; +#endif + + if (!logToConsole) + { + return; + } + + try + { + va_list argptr; + va_start(argptr, pszFormat); + + std::string format; + while (*pszFormat == '\n' || *pszFormat == '\r') + { + if (*pszFormat == '\r') + { + pszTitle = nullptr; + } + + format += *pszFormat++; + } + + if (pszTitle != nullptr) + { + format += pszTitle; + } + + std::string fileNameOnly(fileName); + std::replace(fileNameOnly.begin(), fileNameOnly.end(), '\\', '/'); + + std::string fileNameLineNumber = " " + fileNameOnly.substr(fileNameOnly.find_last_of('/', std::string::npos) + 1) + ":" + std::to_string(lineNumber) + " "; + + format += fileNameLineNumber; + + format += pszFormat; + + if (format.length() < 1 || format[format.length() - 1] != '\n') + { + format += "\n"; + } + + vfprintf(stderr, format.c_str(), argptr); + + va_end(argptr); + } + catch(...) + { + } +} +#define __AZAC_DO_TRACE_IMPL __azac_do_trace_message +#else // __cplusplus +#define __AZAC_DO_TRACE_IMPL +#endif // __cplusplus +#endif + +#define __AZAC_DOTRACE(level, title, fileName, lineNumber, ...) \ + do { \ + __AZAC_DO_TRACE_IMPL(level, title, fileName, lineNumber, ##__VA_ARGS__); \ + } while (0) + +#define __AZAC_TRACE_INFO(title, fileName, lineNumber, msg, ...) __AZAC_DOTRACE(__AZAC_TRACE_LEVEL_INFO, title, fileName, lineNumber, msg, ##__VA_ARGS__) +#define __AZAC_TRACE_INFO_IF(cond, title, fileName, lineNumber, msg, ...) \ + do { \ + int fCond = !!(cond); \ + if (fCond) { \ + __AZAC_TRACE_INFO(title, fileName, lineNumber, msg, ##__VA_ARGS__); \ + } } while (0) + +#define __AZAC_TRACE_WARNING(title, fileName, lineNumber, msg, ...) __AZAC_DOTRACE(__AZAC_TRACE_LEVEL_WARNING, title, fileName, lineNumber, msg, ##__VA_ARGS__) +#define __AZAC_TRACE_WARNING_IF(cond, title, fileName, lineNumber, msg, ...) \ + do { \ + int fCond = !!(cond); \ + if (fCond) { \ + __AZAC_TRACE_WARNING(title, fileName, lineNumber, msg, ##__VA_ARGS__); \ + } } while (0) + +#define __AZAC_TRACE_ERROR(title, fileName, lineNumber, msg, ...) __AZAC_DOTRACE(__AZAC_TRACE_LEVEL_ERROR, title, fileName, lineNumber, msg, ##__VA_ARGS__) +#define __AZAC_TRACE_ERROR_IF(cond, title, fileName, lineNumber, msg, ...) \ + do { \ + int fCond = !!(cond); \ + if (fCond) { \ + __AZAC_TRACE_ERROR(title, fileName, lineNumber, msg, ##__VA_ARGS__); \ + } } while (0) + +#define __AZAC_TRACE_VERBOSE(title, fileName, lineNumber, msg, ...) __AZAC_DOTRACE(__AZAC_TRACE_LEVEL_VERBOSE, title, fileName, lineNumber, msg, ##__VA_ARGS__) +#define __AZAC_TRACE_VERBOSE_IF(cond, title, fileName, lineNumber, msg, ...) \ + do { \ + int fCond = !!(cond); \ + if (fCond) { \ + __AZAC_TRACE_VERBOSE(title, fileName, lineNumber, msg, ##__VA_ARGS__); \ + } } while (0) + +#define ___AZAC_EXPR_AS_STRING(_String) "" #_String +#define __AZAC_EXPR_AS_STRING(_String) ___AZAC_EXPR_AS_STRING(_String) + +#define __AZAC_TRACE_HR(title, fileName, lineNumber, hr, x) __AZAC_TRACE_ERROR(title, fileName, lineNumber, __AZAC_EXPR_AS_STRING(hr) " = 0x%0" PRIxPTR, x) + +#define __AZAC_REPORT_ON_FAIL(title, fileName, lineNumber, hr) \ + do { \ + AZACHR x = hr; \ + if (AZAC_FAILED(x)) { \ + __AZAC_TRACE_HR(title, fileName, lineNumber, hr, x); \ + } } while (0) +#define __AZAC_REPORT_ON_FAIL_IFNOT(title, fileName, lineNumber, hr, hrNot) \ + do { \ + AZACHR x = hr; \ + if (x != hrNot) { \ + if (AZAC_FAILED(x)) { \ + __AZAC_TRACE_HR(title, fileName, lineNumber, hr, x); \ + } } } while (0) + +#define __AZAC_T_RETURN_HR(title, fileName, lineNumber, hr) \ + do { \ + AZACHR x = hr; \ + if (AZAC_FAILED(x)) { \ + __AZAC_TRACE_HR(title, fileName, lineNumber, hr, x); \ + } \ + return x; \ + } while (0) +#define __AZAC_T_RETURN_HR_IF(title, fileName, lineNumber, hr, cond) \ + do { \ + int fCond = !!(cond); \ + if (fCond) { \ + AZACHR x = hr; \ + if (AZAC_FAILED(x)) { \ + __AZAC_TRACE_HR(title, fileName, lineNumber, hr, x); \ + } \ + return x; \ + } } while (0) +#define __AZAC_T_RETURN_ON_FAIL(title, fileName, lineNumber, hr) \ + do { \ + AZACHR x = hr; \ + if (AZAC_FAILED(x)) { \ + __AZAC_TRACE_HR(title, fileName, lineNumber, hr, x); \ + return x; \ + } } while (0) +#define __AZAC_T_RETURN_ON_FAIL_IF_NOT(title, fileName, lineNumber, hr, hrNot) \ + do { \ + AZACHR x = hr; \ + if (x != hrNot) { \ + if (AZAC_FAILED(x)) { \ + __AZAC_TRACE_HR(title, fileName, lineNumber, hr, x); \ + return x; \ + } } } while (0) +#define __AZAC_RETURN_HR(hr) return hr +#define __AZAC_RETURN_HR_IF(hr, cond) \ + do { \ + int fCond = !!(cond); \ + if (fCond) { \ + return hr; \ + } } while (0) +#define __AZAC_RETURN_ON_FAIL(hr) \ + do { \ + AZACHR x = hr; \ + if (AZAC_FAILED(x)) { \ + return x; \ + } } while (0) +#define __AZAC_RETURN_ON_FAIL_IF_NOT(hr, hrNot) \ + do { \ + AZACHR x = hr; \ + if (x != hrNot) { \ + if (AZAC_FAILED(x)) { \ + return x; \ + } } } while (0) + +#define __AZAC_T_EXITFN_HR(title, fileName, lineNumber, hr) \ + do { \ + AZACHR x = hr; \ + if (AZAC_FAILED(x)) { \ + __AZAC_TRACE_HR(title, fileName, lineNumber, hr, x); \ + } \ + goto AZAC_EXITFN_CLEANUP; \ + } while (0) +#define __AZAC_T_EXITFN_HR_IF(title, fileName, lineNumber, hr, cond) \ + do { \ + int fCond = !!(cond); \ + if (fCond) { \ + AZACHR x = hr; \ + if (AZAC_FAILED(x)) { \ + __AZAC_TRACE_HR(title, fileName, lineNumber, hr, x); \ + } \ + goto AZAC_EXITFN_CLEANUP; \ + } } while (0) +#define __AZAC_T_EXITFN_ON_FAIL(title, fileName, lineNumber, hr) \ + do { \ + AZACHR x = hr; \ + if (AZAC_FAILED(x)) { \ + __AZAC_TRACE_HR(title, fileName, lineNumber, hr, x); \ + goto AZAC_EXITFN_CLEANUP; \ + } } while (0) +#define __AZAC_T_EXITFN_ON_FAIL_IF_NOT(title, fileName, lineNumber, hr, hrNot) \ + do { \ + AZACHR x = hr; \ + if (x != hrNot) { \ + if (AZAC_FAILED(x)) { \ + __AZAC_TRACE_HR(title, fileName, lineNumber, hr, x); \ + goto AZAC_EXITFN_CLEANUP; \ + } } } while (0) + +#define __AZAC_EXITFN_HR(hr) \ + do { \ + AZACHR x = hr; \ + goto AZAC_EXITFN_CLEANUP; \ + } while (0) +#define __AZAC_EXITFN_HR_IF(hr, cond) \ + do { \ + int fCond = !!(cond); \ + if (fCond) { \ + AZACHR x = hr; \ + goto AZAC_EXITFN_CLEANUP; \ + } } while (0) +#define __AZAC_EXITFN_ON_FAIL(hr) \ + do { \ + AZACHR x = hr; \ + if (AZAC_FAILED(x)) { \ + goto AZAC_EXITFN_CLEANUP; \ + } } while (0) +#define __AZAC_EXITFN_ON_FAIL_IF_NOT(hr, hrNot) \ + do { \ + AZACHR x = hr; \ + if (x != hrNot) { \ + if (AZAC_FAILED(x)) { \ + goto AZAC_EXITFN_CLEANUP; \ + } } } while (0) + +#define __AZAC_TRACE_ASSERT(title, fileName, lineNumber, expr) __AZAC_TRACE_ERROR(title, fileName, lineNumber, __AZAC_EXPR_AS_STRING(expr) " = false") +#define __AZAC_TRACE_ASSERT_MSG(title, fileName, lineNumber, expr, ...) __AZAC_TRACE_ERROR(title, fileName, lineNumber, __AZAC_EXPR_AS_STRING(expr) " = false; " __VA_ARGS__) + +#define __AZAC_DBG_ASSERT(title, fileName, lineNumber, expr) \ + do { \ + int fCond = !!(expr); \ + if (!fCond) { \ + __AZAC_TRACE_ASSERT(title, fileName, lineNumber, expr); \ + abort(); \ + } } while (0) +#define __AZAC_DBG_ASSERT_WITH_MESSAGE(title, fileName, lineNumber, expr, ...) \ + do { \ + int fCond = !!(expr); \ + if (!fCond) { \ + __AZAC_TRACE_ASSERT_MSG(title, fileName, lineNumber, expr, ##__VA_ARGS__); \ + abort(); \ + } } while (0) + +#define __AZAC_DBG_VERIFY(title, fileName, lineNumber, expr) \ + do { \ + int fCond = !!(expr); \ + if (!fCond) { \ + __AZAC_TRACE_ASSERT(title, fileName, lineNumber, expr); \ + abort(); \ + } } while (0) +#define __AZAC_DBG_VERIFY_WITH_MESSAGE(title, fileName, lineNumber, expr, ...) \ + do { \ + int fCond = !!(expr); \ + if (!fCond) { \ + __AZAC_TRACE_ASSERT_MSG(title, fileName, lineNumber, expr, ##__VA_ARGS__); \ + abort(); \ + } } while (0) + +#ifdef __cplusplus + +#include +#define __AZAC_TRACE_SCOPE(t1, fileName, lineNumber, t2, x, y) \ + __AZAC_TRACE_INFO(t1, fileName, lineNumber, "%s", x); \ + auto evaluateYInScopeInMacros##lineNumber = y; \ + auto leavingScopePrinterInMacros##lineNumber = [&evaluateYInScopeInMacros##lineNumber](int*) -> void { \ + __AZAC_TRACE_INFO(t2, fileName, lineNumber, "%s", evaluateYInScopeInMacros##lineNumber); \ + }; \ + std::unique_ptr onExit##lineNumber((int*)1, leavingScopePrinterInMacros##lineNumber) + +#ifndef __AZAC_THROW_HR_IMPL +#define __AZAC_THROW_HR_IMPL(hr) __azac_rethrow(hr) +#endif +#ifndef __AZAC_THROW_HR +#define __AZAC_THROW_HR(hr) __AZAC_THROW_HR_IMPL(hr) +#endif + +#ifndef __AZAC_LOG_HR_IMPL +#define __AZAC_LOG_HR_IMPL(hr) __azac_log_only(hr) +#endif +#ifndef __AZAC_LOG_HR +#define __AZAC_LOG_HR(hr) __AZAC_LOG_HR_IMPL(hr) +#endif + +#define __AZAC_T_LOG_ON_FAIL(title, fileName, lineNumber, hr) \ + do { \ + AZACHR x = hr; \ + if (AZAC_FAILED(x)) { \ + __AZAC_TRACE_HR(title, fileName, lineNumber, hr, x); \ + __AZAC_LOG_HR(x); \ + } } while (0) +#define __AZAC_T_THROW_ON_FAIL(title, fileName, lineNumber, hr) \ + do { \ + AZACHR x = hr; \ + if (AZAC_FAILED(x)) { \ + __AZAC_TRACE_HR(title, fileName, lineNumber, hr, x); \ + __AZAC_THROW_HR(x); \ + } } while (0) +#define __AZAC_T_THROW_ON_FAIL_IF_NOT(title, fileName, lineNumber, hr, hrNot) \ + do { \ + AZACHR x = hr; \ + if (x != hrNot) { \ + if (AZAC_FAILED(x)) { \ + __AZAC_TRACE_HR(title, fileName, lineNumber, hr, x); \ + __AZAC_THROW_HR(x); \ + } } } while (0) +#define __AZAC_T_THROW_HR_IF(title, fileName, lineNumber, hr, cond) \ + do { \ + int fCond = !!(cond); \ + if (fCond) { \ + AZACHR x = hr; \ + __AZAC_TRACE_HR(title, fileName, lineNumber, hr, x); \ + __AZAC_THROW_HR(x); \ + } } while (0) +#define __AZAC_T_THROW_HR(title, fileName, lineNumber, hr) \ + do { \ + AZACHR x = hr; \ + __AZAC_TRACE_HR(title, fileName, lineNumber, hr, x); \ + __AZAC_THROW_HR(x); \ + } while (0) + + +#define __AZAC_LOG_ON_FAIL(hr) \ + do { \ + AZACHR x = hr; \ + if (AZAC_FAILED(x)) { \ + __AZAC_LOG_HR(x); \ + } } while (0) +#define __AZAC_THROW_ON_FAIL(hr) \ + do { \ + AZACHR x = hr; \ + if (AZAC_FAILED(x)) { \ + __AZAC_THROW_HR(x); \ + } } while (0) +#define __AZAC_THROW_ON_FAIL_IF_NOT(hr, hrNot) \ + do { \ + AZACHR x = hr; \ + if (x != hrNot) { \ + if (AZAC_FAILED(x)) { \ + __AZAC_THROW_HR(x); \ + } } } while (0) +#define __AZAC_THROW_HR_IF(hr, cond) \ + do { \ + int fCond = !!(cond); \ + if (fCond) { \ + AZACHR x = hr; \ + __AZAC_THROW_HR(x); \ + } } while (0) + +#endif // __cplusplus + + + +//------------------------------------------------------- +// AZAC_ macro definitions +//------------------------------------------------------- + +#ifdef AZAC_CONFIG_TRACE_VERBOSE +#define AZAC_TRACE_VERBOSE(msg, ...) __AZAC_TRACE_VERBOSE("AZAC_TRACE_VERBOSE: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define AZAC_TRACE_VERBOSE_IF(cond, msg, ...) __AZAC_TRACE_VERBOSE_IF(cond, "AZAC_TRACE_VERBOSE: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#else +#define AZAC_TRACE_VERBOSE(...) +#define AZAC_TRACE_VERBOSE_IF(...) +#endif + +#ifdef AZAC_CONFIG_DBG_TRACE_VERBOSE +#define AZAC_DBG_TRACE_VERBOSE(msg, ...) __AZAC_TRACE_VERBOSE("AZAC_DBG_TRACE_VERBOSE: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define AZAC_DBG_TRACE_VERBOSE_IF(cond, msg, ...) __AZAC_TRACE_VERBOSE_IF(cond, "AZAC_DBG_TRACE_VERBOSE: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#else +#define AZAC_DBG_TRACE_VERBOSE(...) +#define AZAC_DBG_TRACE_VERBOSE_IF(...) +#endif + +#ifdef AZAC_CONFIG_TRACE_INFO +#define AZAC_TRACE_INFO(msg, ...) __AZAC_TRACE_INFO("AZAC_TRACE_INFO: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define AZAC_TRACE_INFO_IF(cond, msg, ...) __AZAC_TRACE_INFO_IF(cond, "AZAC_TRACE_INFO: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#else +#define AZAC_TRACE_INFO(...) +#define AZAC_TRACE_INFO_IF(...) +#endif + +#ifdef AZAC_CONFIG_DBG_TRACE_INFO +#define AZAC_DBG_TRACE_INFO(msg, ...) __AZAC_TRACE_INFO("AZAC_DBG_TRACE_INFO: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define AZAC_DBG_TRACE_INFO_IF(cond, msg, ...) __AZAC_TRACE_INFO_IF(cond, "AZAC_DBG_TRACE_INFO: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#else +#define AZAC_DBG_TRACE_INFO(...) +#define AZAC_DBG_TRACE_INFO_IF(...) +#endif + +#ifdef AZAC_CONFIG_TRACE_WARNING +#define AZAC_TRACE_WARNING(msg, ...) __AZAC_TRACE_WARNING("AZAC_TRACE_WARNING:", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define AZAC_TRACE_WARNING_IF(cond, msg, ...) __AZAC_TRACE_WARNING_IF(cond, "AZAC_TRACE_WARNING:", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#else +#define AZAC_TRACE_WARNING(...) +#define AZAC_TRACE_WARNING_IF(...) +#endif + +#ifdef AZAC_CONFIG_DBG_TRACE_WARNING +#define AZAC_DBG_TRACE_WARNING(msg, ...) __AZAC_TRACE_WARNING("AZAC_DBG_TRACE_WARNING:", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define AZAC_DBG_TRACE_WARNING_IF(cond, msg, ...) __AZAC_TRACE_WARNING_IF(cond, "AZAC_DBG_TRACE_WARNING:", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#else +#define AZAC_DBG_TRACE_WARNING(...) +#define AZAC_DBG_TRACE_WARNING_IF(...) +#endif + +#ifdef AZAC_CONFIG_TRACE_ERROR +#define AZAC_TRACE_ERROR(msg, ...) __AZAC_TRACE_ERROR("AZAC_TRACE_ERROR: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define AZAC_TRACE_ERROR_IF(cond, msg, ...) __AZAC_TRACE_ERROR_IF(cond, "AZAC_TRACE_ERROR: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#else +#define AZAC_TRACE_ERROR(...) +#define AZAC_TRACE_ERROR_IF(...) +#endif + +#ifdef AZAC_CONFIG_DBG_TRACE_ERROR +#define AZAC_DBG_TRACE_ERROR(msg, ...) __AZAC_TRACE_ERROR("AZAC_DBG_TRACE_ERROR: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define AZAC_DBG_TRACE_ERROR_IF(cond, msg, ...) __AZAC_TRACE_ERROR_IF(cond, "AZAC_DBG_TRACE_ERROR: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#else +#define AZAC_DBG_TRACE_ERROR(...) +#define AZAC_DBG_TRACE_ERROR_IF(...) +#endif + +#ifdef AZAC_CONFIG_TRACE_FUNCTION +#define AZAC_TRACE_FUNCTION(...) __AZAC_TRACE_VERBOSE("AZAC_TRACE_FUNCTION: ", __FILE__, __LINE__, __FUNCTION__) +#else +#define AZAC_TRACE_FUNCTION(...) +#endif + +#ifdef AZAC_CONFIG_DBG_TRACE_FUNCTION +#define AZAC_DBG_TRACE_FUNCTION(...) __AZAC_TRACE_VERBOSE("AZAC_DBG_TRACE_FUNCTION: ", __FILE__, __LINE__, __FUNCTION__) +#else +#define AZAC_DBG_TRACE_FUNCTION(...) +#endif + +#ifdef AZAC_CONFIG_TRACE_REPORT_ON_FAIL +#define AZAC_REPORT_ON_FAIL(hr) __AZAC_REPORT_ON_FAIL("AZAC_REPORT_ON_FAIL: ", __FILE__, __LINE__, hr) +#define AZAC_REPORT_ON_FAIL_IFNOT(hr, hrNot) __AZAC_REPORT_ON_FAIL_IFNOT("AZAC_REPORT_ON_FAIL: ", __FILE__, __LINE__, hr, hrNot) +#else +#define AZAC_REPORT_ON_FAIL(hr) UNUSED(hr) +#define AZAC_REPORT_ON_FAIL_IFNOT(hr, hrNot) UNUSED(hr); UNUSED(hrNot) +#endif + +#ifdef AZAC_CONFIG_TRACE_RETURN_ON_FAIL +#define AZAC_RETURN_HR(hr) __AZAC_T_RETURN_HR("AZAC_RETURN_ON_FAIL: ", __FILE__, __LINE__, hr) +#define AZAC_RETURN_HR_IF(hr, cond) __AZAC_T_RETURN_HR_IF("AZAC_RETURN_ON_FAIL: ", __FILE__, __LINE__, hr, cond) +#define AZAC_RETURN_ON_FAIL(hr) __AZAC_T_RETURN_ON_FAIL("AZAC_RETURN_ON_FAIL: ", __FILE__, __LINE__, hr) +#define AZAC_RETURN_ON_FAIL_IF_NOT(hr, hrNot) __AZAC_T_RETURN_ON_FAIL_IF_NOT("AZAC_RETURN_ON_FAIL: ", __FILE__, __LINE__, hr, hrNot) +#else +#define AZAC_RETURN_HR(hr) __AZAC_RETURN_HR(hr) +#define AZAC_RETURN_HR_IF(hr, cond) __AZAC_RETURN_HR_IF(hr, cond) +#define AZAC_RETURN_ON_FAIL(hr) __AZAC_RETURN_ON_FAIL(hr) +#define AZAC_RETURN_ON_FAIL_IF_NOT(hr, hrNot) __AZAC_RETURN_ON_FAIL_IF_NOT(hr, hrNot) +#endif + +#define AZAC_IFTRUE_RETURN_HR(cond, hr) AZAC_RETURN_HR_IF(hr, cond) +#define AZAC_IFFALSE_RETURN_HR(cond, hr) AZAC_RETURN_HR_IF(hr, !(cond)) +#define AZAC_IFFAILED_RETURN_HR(hr) AZAC_RETURN_ON_FAIL(hr) +#define AZAC_IFFAILED_RETURN_HR_IFNOT(hr, hrNot) AZAC_RETURN_ON_FAIL_IF_NOT(hr, hrNot) + +#ifdef AZAC_CONFIG_TRACE_EXITFN_ON_FAIL +#define AZAC_EXITFN_HR(hr) __AZAC_T_EXITFN_HR("AZAC_EXITFN_ON_FAIL: ", __FILE__, __LINE__, hr) +#define AZAC_EXITFN_HR_IF(hr, cond) __AZAC_T_EXITFN_HR_IF("AZAC_EXITFN_ON_FAIL: ", __FILE__, __LINE__, hr, cond) +#define AZAC_EXITFN_ON_FAIL(hr) __AZAC_T_EXITFN_ON_FAIL("AZAC_EXITFN_ON_FAIL: ", __FILE__, __LINE__, hr) +#define AZAC_EXITFN_ON_FAIL_IF_NOT(hr, hrNot) __AZAC_EXITFN_ON_FAIL_IF_NOT("AZAC_EXITFN_ON_FAIL: ", __FILE__, __LINE__, hr, hrNot) +#else +#define AZAC_EXITFN_HR(hr) __AZAC_EXITFN_HR(hr) +#define AZAC_EXITFN_HR_IF(hr, cond) __AZAC_EXITFN_HR_IF(hr, cond) +#define AZAC_EXITFN_ON_FAIL(hr) __AZAC_EXITFN_ON_FAIL(hr) +#define AZAC_EXITFN_ON_FAIL_IF_NOT(hr, hrNot) __AZAC_EXITFN_ON_FAIL_IF_NOT(hr, hrNot) +#endif + +#define AZAC_IFTRUE_EXITFN_WHR(cond, hr) AZAC_EXITFN_HR_IF(hr, cond) +#define AZAC_IFFALSE_EXITFN_WHR(cond, hr) AZAC_EXITFN_HR_IF(hr, !(cond)) +#define AZAC_IFFAILED_EXITFN_WHR(hr) AZAC_EXITFN_ON_FAIL(hr) +#define AZAC_IFFAILED_EXITFN_WHR_IFNOT(hr, hrNot) AZAC_EXITFN_ON_FAIL_IF_NOT(hr, hrNot) + +#define AZAC_IFTRUE_EXITFN_CLEANUP(cond, expr) \ + do { \ + int fCondT = !!(cond); \ + if (fCondT) { \ + expr; \ + goto AZAC_EXITFN_CLEANUP; \ + } } while (0) + +#define AZAC_IFFALSE_EXITFN_CLEANUP(cond, expr) \ + do { \ + int fCondF = !!(cond); \ + if (!fCondF) { \ + expr; \ + goto AZAC_EXITFN_CLEANUP; \ + } } while (0) + +#if defined(AZAC_CONFIG_DBG_TRACE_ASSERT) && (defined(DEBUG) || defined(_DEBUG)) +#define AZAC_DBG_ASSERT(expr) __AZAC_DBG_ASSERT("AZAC_ASSERT: ", __FILE__, __LINE__, expr) +#define AZAC_DBG_ASSERT_WITH_MESSAGE(expr, ...) __AZAC_DBG_ASSERT_WITH_MESSAGE("AZAC_ASSERT: ", __FILE__, __LINE__, expr, ##__VA_ARGS__) +#else +#define AZAC_DBG_ASSERT(expr) +#define AZAC_DBG_ASSERT_WITH_MESSAGE(expr, ...) +#endif + +#if defined(AZAC_CONFIG_DBG_TRACE_VERIFY) && (defined(DEBUG) || defined(_DEBUG)) +#define AZAC_DBG_VERIFY(expr) __AZAC_DBG_VERIFY("AZAC_VERIFY: ", __FILE__, __LINE__, expr) +#define AZAC_DBG_VERIFY_WITH_MESSAGE(expr, ...) __AZAC_DBG_VERIFY_WITH_MESSAGE("AZAC_VERIFY: ", __FILE__, __LINE__, expr, ##__VA_ARGS__) +#else +#define AZAC_DBG_VERIFY(expr) (expr) +#define AZAC_DBG_VERIFY_WITH_MESSAGE(expr, ...) (expr) +#endif + +#define AZAC_IFTRUE(cond, expr) \ + do { \ + int fCondT = !!(cond); \ + if (fCondT) { \ + expr; \ + } } while (0) + +#define AZAC_IFFALSE(cond, expr) \ + do { \ + int fCondF = !!(cond); \ + if (!fCondF) { \ + expr; \ + } } while (0) + +// handle circular dependency +#ifndef AZAC_SUPPRESS_COMMON_INCLUDE_FROM_DEBUG +#define AZAC_SUPPRESS_DEBUG_INCLUDE_FROM_COMMON +#include +#undef AZAC_SUPPRESS_DEBUG_INCLUDE_FROM_COMMON +#endif + +#ifdef __cplusplus + +#ifdef AZAC_CONFIG_TRACE_SCOPE +#define AZAC_TRACE_SCOPE(x, y) __AZAC_TRACE_SCOPE("AZAC_TRACE_SCOPE_ENTER: ", __FILE__, __LINE__, "AZAC_TRACE_SCOPE_EXIT: ", x, y) +#else +#define AZAC_TRACE_SCOPE(x, y) +#endif + +#ifdef AZAC_CONFIG_DBG_TRACE_SCOPE +#define AZAC_DBG_TRACE_SCOPE(x, y) __AZAC_TRACE_SCOPE("AZAC_DBG_TRACE_SCOPE_ENTER: ", __FILE__, __LINE__, "AZAC_DBG_TRACE_SCOPE_EXIT: ", x, y) +#else +#define AZAC_DBG_TRACE_SCOPE(x, y) +#endif + +#ifdef AZAC_CONFIG_TRACE_THROW_ON_FAIL +#define AZAC_THROW_ON_FAIL(hr) __AZAC_T_THROW_ON_FAIL("AZAC_THROW_ON_FAIL: ", __FILE__, __LINE__, hr) +#define AZAC_THROW_ON_FAIL_IF_NOT(hr, hrNot) __AZAC_T_THROW_ON_FAIL_IF_NOT("AZAC_THROW_ON_FAIL: ", __FILE__, __LINE__, hr, hrNot) +#define AZAC_LOG_ON_FAIL(hr) __AZAC_T_LOG_ON_FAIL("AZAC_LOG_ON_FAIL: ", __FILE__, __LINE__, hr) +#define AZAC_THROW_HR_IF(hr, cond) __AZAC_T_THROW_HR_IF("AZAC_THROW_HR_IF: ", __FILE__, __LINE__, hr, cond) +#define AZAC_THROW_HR(hr) __AZAC_T_THROW_HR("AZAC_THROW_HR: ", __FILE__, __LINE__, hr) +#else +#define AZAC_THROW_ON_FAIL(hr) __AZAC_THROW_ON_FAIL(hr) +#define AZAC_THROW_ON_FAIL_IF_NOT(hr, hrNot) __AZAC_THROW_ON_FAIL_IF_NOT(hr, hrNot) +#define AZAC_LOG_ON_FAIL(hr) __AZAC_LOG_ON_FAIL(hr) +#define AZAC_THROW_HR_IF(hr, cond) __AZAC_THROW_HR_IF(hr, cond) +#define AZAC_THROW_HR(hr) __AZAC_THROW_HR(hr) +#endif + +#define AZAC_IFTRUE_THROW_HR(cond, hr) AZAC_THROW_HR_IF(hr, cond) +#define AZAC_IFFALSE_THROW_HR(cond, hr) AZAC_THROW_HR_IF(hr, !(cond)) +#define AZAC_IFFAILED_THROW_HR(hr) AZAC_THROW_ON_FAIL(hr) +#define AZAC_IFFAILED_THROW_HR_IFNOT(hr, hrNot) AZAC_THROW_ON_FAIL_IF_NOT(hr, hrNot) + +#include +#include +#include +#include + +inline void __azac_handle_native_ex(AZACHR hr, bool throwException) +{ + AZAC_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + auto handle = reinterpret_cast(hr); + auto error = error_get_error_code(handle); + if (error == AZAC_ERR_NONE) + { + if (throwException) + { + throw hr; + } + else + { + // do nothing. This is already logged by the macros that call this function + return; + } + } + + std::string errorMsg; + try + { + auto callstack = error_get_call_stack(handle); + auto what = error_get_message(handle); + + if (what) + { + errorMsg += what; + } + else + { + errorMsg += "Exception with error code: "; + errorMsg += std::to_string(error); + } + + if (callstack) + { + errorMsg += callstack; + } + } + catch (...) + { + error_release(handle); + throw hr; + } + + error_release(handle); + if (throwException) + { + throw std::runtime_error(errorMsg); + } + else + { + AZAC_TRACE_ERROR("Error details: %s", errorMsg.c_str()); + } +} + +inline void __azac_log_only(AZACHR hr) +{ + __azac_handle_native_ex(hr, false); +} + +inline void __azac_rethrow(AZACHR hr) +{ + __azac_handle_native_ex(hr, true); +} + +#else // __cplusplus + +#define AZAC_TRACE_SCOPE(x, y) static_assert(false) +#define AZAC_DBG_TRACE_SCOPE(x, y) static_assert(false) +#define AZAC_LOG_ON_FAIL(hr) static_assert(false) +#define AZAC_THROW_ON_FAIL(hr) static_assert(false) +#define AZAC_THROW_ON_FAIL_IF_NOT(hr, hrNot) static_assert(false) +#define AZAC_THROW_HR_IF(hr, cond) static_assert(false) +#define AZAC_THROW_HR(hr) static_assert(false) +#define AZAC_IFTRUE_THROW_HR(cond, hr) static_assert(false) +#define AZAC_IFFALSE_THROW_HR(cond, hr) static_assert(false) +#define AZAC_IFFAILED_THROW_HR(hr) static_assert(false) +#define AZAC_IFFAILED_THROW_HR_IFNOT(hr, hrNot) static_assert(false) + +#endif // __cplusplus + +//--------------------------------------------------------------------------- + +#ifdef __AZAC_DEBUG_H_EXAMPLES_IN_MAIN + +void main() +{ + int x = 4; + printf("%s = %d\n", __AZAC_EXPR_AS_STRING(x + 3), x + 3); + + AZAC_TRACE_INFO("hello there"); + AZAC_TRACE_ERROR("hello there"); + AZAC_TRACE_WARNING("hello there"); + AZAC_TRACE_VERBOSE("hello there"); + + AZAC_TRACE_INFO("hello there %d", 5); + AZAC_TRACE_ERROR("hello there %d", 5); + AZAC_TRACE_WARNING("hello there %d", 5); + AZAC_TRACE_VERBOSE("hello there %d", 5); + + AZAC_TRACE_INFO_IF(false, "hello there false"); + AZAC_TRACE_ERROR_IF(false, "hello there false"); + AZAC_TRACE_WARNING_IF(false, "hello there false"); + AZAC_TRACE_VERBOSE_IF(false, "hello there false"); + + AZAC_TRACE_INFO_IF(false, "hello there false %d", 5); + AZAC_TRACE_ERROR_IF(false, "hello there false %d", 5); + AZAC_TRACE_WARNING_IF(false, "hello there false %d", 5); + AZAC_TRACE_VERBOSE_IF(false, "hello there false %d", 5); + + AZAC_TRACE_INFO_IF(true, "hello there true"); + AZAC_TRACE_ERROR_IF(true, "hello there true"); + AZAC_TRACE_WARNING_IF(true, "hello there true"); + AZAC_TRACE_VERBOSE_IF(true, "hello there true"); + + AZAC_TRACE_INFO_IF(true, "hello there true %d", 5); + AZAC_TRACE_ERROR_IF(true, "hello there true %d", 5); + AZAC_TRACE_WARNING_IF(true, "hello there true %d", 5); + AZAC_TRACE_VERBOSE_IF(true, "hello there true %d", 5); + + AZAC_DBG_TRACE_INFO("hello there"); + AZAC_DBG_TRACE_ERROR("hello there"); + AZAC_DBG_TRACE_WARNING("hello there"); + AZAC_DBG_TRACE_VERBOSE("hello there"); + + AZAC_DBG_TRACE_INFO("hello there %d", 5); + AZAC_DBG_TRACE_ERROR("hello there %d", 5); + AZAC_DBG_TRACE_WARNING("hello there %d", 5); + AZAC_DBG_TRACE_VERBOSE("hello there %d", 5); + + AZAC_DBG_TRACE_INFO_IF(false, "hello there false"); + AZAC_DBG_TRACE_ERROR_IF(false, "hello there false"); + AZAC_DBG_TRACE_WARNING_IF(false, "hello there false"); + AZAC_DBG_TRACE_VERBOSE_IF(false, "hello there false"); + + AZAC_DBG_TRACE_INFO_IF(false, "hello there false %d", 5); + AZAC_DBG_TRACE_ERROR_IF(false, "hello there false %d", 5); + AZAC_DBG_TRACE_WARNING_IF(false, "hello there false %d", 5); + AZAC_DBG_TRACE_VERBOSE_IF(false, "hello there false %d", 5); + + AZAC_DBG_TRACE_INFO_IF(true, "hello there true"); + AZAC_DBG_TRACE_ERROR_IF(true, "hello there true"); + AZAC_DBG_TRACE_WARNING_IF(true, "hello there true"); + AZAC_DBG_TRACE_VERBOSE_IF(true, "hello there true"); + + AZAC_DBG_TRACE_INFO_IF(true, "hello there true %d", 5); + AZAC_DBG_TRACE_ERROR_IF(true, "hello there true %d", 5); + AZAC_DBG_TRACE_WARNING_IF(true, "hello there true %d", 5); + AZAC_DBG_TRACE_VERBOSE_IF(true, "hello there true %d", 5); + + AZAC_TRACE_SCOPE("A", "B"); + + AZAC_TRACE_FUNCTION(); + AZAC_DBG_TRACE_FUNCTION(); + + AZAC_DBG_ASSERT(false); + AZAC_DBG_ASSERT(true); + + AZAC_DBG_ASSERT_WITH_MESSAGE(false, "HEY!"); + AZAC_DBG_ASSERT_WITH_MESSAGE(true, "HEY!!"); + + AZAC_DBG_VERIFY(false); + AZAC_DBG_VERIFY(true); + + AZAC_DBG_VERIFY_WITH_MESSAGE(false, "HEY!"); + AZAC_DBG_VERIFY_WITH_MESSAGE(true, "HEY!!"); + + AZACHR hr1 { 0x80001111 }; + AZACHR hr2 { 0x00001111 }; + + AZAC_TRACE_VERBOSE("Testing out AZAC_REPORT_ON_FAIL, should see two failures..."); + AZAC_REPORT_ON_FAIL(hr1); + AZAC_REPORT_ON_FAIL_IFNOT(hr1, 0x80001000); + AZAC_TRACE_VERBOSE("Testing out AZAC_REPORT_ON_FAIL, should see two failures... Done!"); + + AZAC_TRACE_VERBOSE("Testing out AZAC_REPORT_ON_FAIL, should see zero failures..."); + AZAC_REPORT_ON_FAIL(hr2); + AZAC_REPORT_ON_FAIL_IFNOT(hr1, 0x80001111); + AZAC_REPORT_ON_FAIL_IFNOT(hr2, 0x80001111); + AZAC_REPORT_ON_FAIL_IFNOT(hr2, 0x80001000); + AZAC_TRACE_VERBOSE("Testing out AZAC_REPORT_ON_FAIL, should see zero failures... Done!"); +} + +#endif diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_error.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_error.h new file mode 100644 index 0000000..61ec189 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/azac_error.h @@ -0,0 +1,455 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/azai/license202106 for the full license information. + +#pragma once + +// TODO: TFS#3671215 - Vision: C/C++ azac_api* files are in shared include directory, speech and vision share + +#include + +/// +/// Type definition for Azure AI Core result codes. +/// +typedef uintptr_t AZACHR; + +/// +/// Default result code indicating no error. +/// +#define AZAC_ERR_NONE 0 + +/// +/// Declare and initialize result code variable. +/// +#define AZAC_INIT_HR(hr) AZACHR hr = AZAC_ERR_NONE; \ + (void)(hr) + +/// +/// Check if result code indicates success. +/// +#define AZAC_SUCCEEDED(x) ((x) == AZAC_ERR_NONE) + +/// +/// Check if result code indicates error. +/// +#define AZAC_FAILED(x) (!AZAC_SUCCEEDED(x)) + +/// +/// Base macros for all error codes. +/// +#define __AZAC_ERRCODE_FAILED(x) (x) + +/// +/// The function is not implemented. +/// +#define AZAC_ERR_NOT_IMPL __AZAC_ERRCODE_FAILED(0xfff) + +/// +/// The object has not been properly initialized. +/// +#define AZAC_ERR_UNINITIALIZED __AZAC_ERRCODE_FAILED(0x001) + +/// +/// The object has already been initialized. +/// +#define AZAC_ERR_ALREADY_INITIALIZED __AZAC_ERRCODE_FAILED(0x002) + +/// +/// An unhandled exception was detected. +/// +#define AZAC_ERR_UNHANDLED_EXCEPTION __AZAC_ERRCODE_FAILED(0x003) + +/// +/// The object or property was not found. +/// +#define AZAC_ERR_NOT_FOUND __AZAC_ERRCODE_FAILED(0x004) + +/// +/// One or more arguments are not valid. +/// +#define AZAC_ERR_INVALID_ARG __AZAC_ERRCODE_FAILED(0x005) + +/// +/// The specified timeout value has elapsed. +/// +#define AZAC_ERR_TIMEOUT __AZAC_ERRCODE_FAILED(0x006) + +/// +/// The asynchronous operation is already in progress. +/// +#define AZAC_ERR_ALREADY_IN_PROGRESS __AZAC_ERRCODE_FAILED(0x007) + +/// +/// The attempt to open the file failed. +/// +#define AZAC_ERR_FILE_OPEN_FAILED __AZAC_ERRCODE_FAILED(0x008) + +/// +/// The end of the file was reached unexpectedly. +/// +#define AZAC_ERR_UNEXPECTED_EOF __AZAC_ERRCODE_FAILED(0x009) + +/// +/// Invalid audio header encountered. +/// +#define AZAC_ERR_INVALID_HEADER __AZAC_ERRCODE_FAILED(0x00a) + +/// +/// The requested operation cannot be performed while audio is pumping +/// +#define AZAC_ERR_AUDIO_IS_PUMPING __AZAC_ERRCODE_FAILED(0x00b) + +/// +/// Unsupported audio format. +/// +#define AZAC_ERR_UNSUPPORTED_FORMAT __AZAC_ERRCODE_FAILED(0x00c) + +/// +/// Operation aborted. +/// +#define AZAC_ERR_ABORT __AZAC_ERRCODE_FAILED(0x00d) + +/// +/// Microphone is not available. +/// +#define AZAC_ERR_MIC_NOT_AVAILABLE __AZAC_ERRCODE_FAILED(0x00e) + +/// +/// An invalid state was encountered. +/// +#define AZAC_ERR_INVALID_STATE __AZAC_ERRCODE_FAILED(0x00f) + +/// +/// Attempting to create a UUID failed. +/// +#define AZAC_ERR_UUID_CREATE_FAILED __AZAC_ERRCODE_FAILED(0x010) + +/// +/// An unexpected session state transition was encountered when setting the session audio format. +/// +/// +/// Valid transitions are: +/// * WaitForPumpSetFormatStart --> ProcessingAudio (at the beginning of stream) +/// * StoppingPump --> WaitForAdapterCompletedSetFormatStop (at the end of stream) +/// * ProcessingAudio --> WaitForAdapterCompletedSetFormatStop (when the stream runs out of data) +/// All other state transitions are invalid. +/// +#define AZAC_ERR_SETFORMAT_UNEXPECTED_STATE_TRANSITION __AZAC_ERRCODE_FAILED(0x011) + +/// +/// An unexpected session state was encountered in while processing audio. +/// +/// +/// Valid states to encounter are: +/// * ProcessingAudio: We're allowed to process audio while in this state. +/// * StoppingPump: We're allowed to be called to process audio, but we'll ignore the data passed in while we're attempting to stop the pump. +/// All other states are invalid while processing audio. +/// +#define AZAC_ERR_PROCESS_AUDIO_INVALID_STATE __AZAC_ERRCODE_FAILED(0x012) + +/// +/// An unexpected state transition was encountered while attempting to start recognizing. +/// +/// +/// A valid transition is: +/// * Idle --> WaitForPumpSetFormatStart +/// All other state transitions are invalid when attempting to start recognizing +/// +#define AZAC_ERR_START_RECOGNIZING_INVALID_STATE_TRANSITION __AZAC_ERRCODE_FAILED(0x013) + +/// +/// An unexpected error was encountered when trying to create an internal object. +/// +#define AZAC_ERR_UNEXPECTED_CREATE_OBJECT_FAILURE __AZAC_ERRCODE_FAILED(0x014) + +/// +/// An error in the audio-capturing system. +/// +#define AZAC_ERR_MIC_ERROR __AZAC_ERRCODE_FAILED(0x015) + +/// +/// The requested operation cannot be performed; there is no audio input. +/// +#define AZAC_ERR_NO_AUDIO_INPUT __AZAC_ERRCODE_FAILED(0x016) + +/// +/// An unexpected error was encountered when trying to access the USP site. +/// +#define AZAC_ERR_UNEXPECTED_USP_SITE_FAILURE __AZAC_ERRCODE_FAILED(0x017) + +/// +/// An unexpected error was encountered when trying to access the LU site. +/// +#define AZAC_ERR_UNEXPECTED_LU_SITE_FAILURE __AZAC_ERRCODE_FAILED(0x018) + +/// +/// The buffer is too small. +/// +#define AZAC_ERR_BUFFER_TOO_SMALL __AZAC_ERRCODE_FAILED(0x019) + +/// +/// A method failed to allocate memory. +/// +#define AZAC_ERR_OUT_OF_MEMORY __AZAC_ERRCODE_FAILED(0x01A) + +/// +/// An unexpected runtime error occurred. +/// +#define AZAC_ERR_RUNTIME_ERROR __AZAC_ERRCODE_FAILED(0x01B) + +/// +/// The url specified is invalid. +/// +#define AZAC_ERR_INVALID_URL __AZAC_ERRCODE_FAILED(0x01C) + +/// +/// The region specified is invalid or missing. +/// +#define AZAC_ERR_INVALID_REGION __AZAC_ERRCODE_FAILED(0x01D) + +/// +/// Switch between single shot and continuous recognition is not supported. +/// +#define AZAC_ERR_SWITCH_MODE_NOT_ALLOWED __AZAC_ERRCODE_FAILED(0x01E) + +/// +/// Changing connection status is not supported in the current recognition state. +/// +#define AZAC_ERR_CHANGE_CONNECTION_STATUS_NOT_ALLOWED __AZAC_ERRCODE_FAILED(0x01F) + +/// +/// Explicit connection management is not supported by the specified recognizer. +/// +#define AZAC_ERR_EXPLICIT_CONNECTION_NOT_SUPPORTED_BY_RECOGNIZER __AZAC_ERRCODE_FAILED(0x020) + +/// +/// The handle is invalid. +/// +#define AZAC_ERR_INVALID_HANDLE __AZAC_ERRCODE_FAILED(0x021) + +/// +/// The recognizer is invalid. +/// +#define AZAC_ERR_INVALID_RECOGNIZER __AZAC_ERRCODE_FAILED(0x022) + +/// +/// The value is out of range. +/// Added in version 1.3.0. +/// +#define AZAC_ERR_OUT_OF_RANGE __AZAC_ERRCODE_FAILED(0x023) + +/// +/// Extension library not found. +/// Added in version 1.3.0. +/// +#define AZAC_ERR_EXTENSION_LIBRARY_NOT_FOUND __AZAC_ERRCODE_FAILED(0x024) + +/// +/// An unexpected error was encountered when trying to access the TTS engine site. +/// Added in version 1.4.0. +/// +#define AZAC_ERR_UNEXPECTED_TTS_ENGINE_SITE_FAILURE __AZAC_ERRCODE_FAILED(0x025) + +/// +/// An unexpected error was encountered when trying to access the audio output stream. +/// Added in version 1.4.0. +/// +#define AZAC_ERR_UNEXPECTED_AUDIO_OUTPUT_FAILURE __AZAC_ERRCODE_FAILED(0x026) + +/// +/// Gstreamer internal error. +/// Added in version 1.4.0. +/// +#define AZAC_ERR_GSTREAMER_INTERNAL_ERROR __AZAC_ERRCODE_FAILED(0x027) + +/// +/// Compressed container format not supported. +/// Added in version 1.4.0. +/// +#define AZAC_ERR_CONTAINER_FORMAT_NOT_SUPPORTED_ERROR __AZAC_ERRCODE_FAILED(0x028) + +/// +/// Codec extension or gstreamer not found. +/// Added in version 1.4.0. +/// +#define AZAC_ERR_GSTREAMER_NOT_FOUND_ERROR __AZAC_ERRCODE_FAILED(0x029) + +/// +/// The language specified is missing. +/// Added in version 1.5.0. +/// +#define AZAC_ERR_INVALID_LANGUAGE __AZAC_ERRCODE_FAILED(0x02A) + +/// +/// The API is not applicable. +/// Added in version 1.5.0. +/// +#define AZAC_ERR_UNSUPPORTED_API_ERROR __AZAC_ERRCODE_FAILED(0x02B) + +/// +/// The ring buffer is unavailable. +/// Added in version 1.8.0. +/// +#define AZAC_ERR_RINGBUFFER_DATA_UNAVAILABLE __AZAC_ERRCODE_FAILED(0x02C) + +/// +/// An unexpected error was encountered when trying to access the Conversation site. +/// Added in version 1.5.0. +/// +#define AZAC_ERR_UNEXPECTED_CONVERSATION_SITE_FAILURE __AZAC_ERRCODE_FAILED(0x030) + +/// +/// An unexpected error was encountered when trying to access the Conversation site. +/// Added in version 1.8.0. +/// +#define AZAC_ERR_UNEXPECTED_CONVERSATION_TRANSLATOR_SITE_FAILURE __AZAC_ERRCODE_FAILED(0x031) + +/// +/// An asynchronous operation was canceled before it was executed. +/// Added in version 1.8.0. +/// +#define AZAC_ERR_CANCELED __AZAC_ERRCODE_FAILED(0x032) + +/// +/// Codec for compression could not be initialized. +/// Added in version 1.10.0. +/// +#define AZAC_ERR_COMPRESS_AUDIO_CODEC_INITIFAILED __AZAC_ERRCODE_FAILED(0x033) + +/// +/// Data not available. +/// Added in version 1.10.0. +/// +#define AZAC_ERR_DATA_NOT_AVAILABLE __AZAC_ERRCODE_FAILED(0x034) + +/// +/// Invalid result reason. +/// Added in version 1.12.0 +/// +#define AZAC_ERR_INVALID_RESULT_REASON __AZAC_ERRCODE_FAILED(0x035) + +/// +/// An unexpected error was encountered when trying to access the RNN-T site. +/// +#define AZAC_ERR_UNEXPECTED_RNNT_SITE_FAILURE __AZAC_ERRCODE_FAILED(0x036) + +/// +/// Sending of a network message failed. +/// +#define AZAC_ERR_NETWORK_SEND_FAILED __AZAC_ERRCODE_FAILED(0x037) + +/// +/// Audio extension library not found. +/// Added in version 1.16.0. +/// +#define AZAC_ERR_AUDIO_SYS_LIBRARY_NOT_FOUND __AZAC_ERRCODE_FAILED(0x038) + +/// +/// An error in the audio-rendering system. +/// Added in version 1.20.0 +/// +#define AZAC_ERR_LOUDSPEAKER_ERROR __AZAC_ERRCODE_FAILED(0x039) + +/// +/// An unexpected error was encountered when trying to access the Vision site. +/// Added in version 1.15.0. +/// +#define AZAC_ERR_VISION_SITE_FAILURE __AZAC_ERRCODE_FAILED(0x050) + +/// +/// Stream number provided was invalid in the current context. +/// Added in version 1.15.0. +/// +#define AZAC_ERR_MEDIA_INVALID_STREAM __AZAC_ERRCODE_FAILED(0x060) + +/// +/// Offset required is invalid in the current context. +/// Added in version 1.15.0. +/// +#define AZAC_ERR_MEDIA_INVALID_OFFSET __AZAC_ERRCODE_FAILED(0x061) + +/// +/// No more data is available in source. +/// Added in version 1.15.0. +/// +#define AZAC_ERR_MEDIA_NO_MORE_DATA __AZAC_ERRCODE_FAILED(0x062) + +/// +/// Source has not been started. +/// Added in version 1.15.0. +/// +#define AZAC_ERR_MEDIA_NOT_STARTED __AZAC_ERRCODE_FAILED(0x063) + +/// +/// Source has already been started. +/// Added in version 1.15.0. +/// +#define AZAC_ERR_MEDIA_ALREADY_STARTED __AZAC_ERRCODE_FAILED(0x064) + +/// +/// Media device creation failed. +/// Added in version 1.18.0. +/// +#define AZAC_ERR_MEDIA_DEVICE_CREATION_FAILED __AZAC_ERRCODE_FAILED(0x065) + +/// +/// No devices of the selected category are available. +/// Added in version 1.18.0. +/// +#define AZAC_ERR_MEDIA_NO_DEVICE_AVAILABLE __AZAC_ERRCODE_FAILED(0x066) + +/// +/// Enabled Voice Activity Detection while using keyword recognition is not allowed. +/// +#define AZAC_ERR_VAD_COULD_NOT_USE_WITH_KEYWORD_RECOGNIZER __AZAC_ERRCODE_FAILED(0x067) + +/// +/// The specified RecoEngineAdapter could not be created. +/// +#define AZAC_ERR_COULD_NOT_CREATE_ENGINE_ADAPTER __AZAC_ERRCODE_FAILED(0x070) + +/// +/// The input file has a size of 0 bytes. +/// +#define AZAC_ERR_INPUT_FILE_SIZE_IS_ZERO_BYTES __AZAC_ERRCODE_FAILED(0x072) + +/// +/// Cannot open the input media file for reading. Does it exist? +/// +#define AZAC_ERR_FAILED_TO_OPEN_INPUT_FILE_FOR_READING __AZAC_ERRCODE_FAILED(0x073) + +/// +/// Failed to read from the input media file. +/// +#define AZAC_ERR_FAILED_TO_READ_FROM_INPUT_FILE __AZAC_ERRCODE_FAILED(0x074) + +/// +/// Input media file is too large. +/// +#define AZAC_ERR_INPUT_FILE_TOO_LARGE __AZAC_ERRCODE_FAILED(0x075) + +/// +/// The input URL is unsupported. It should start with `http://`, `https://` or `rtsp://`. +/// +#define AZAC_ERR_UNSUPPORTED_URL_PROTOCOL __AZAC_ERRCODE_FAILED(0x076) + +/// +/// The Nullable value is empty. Check HasValue() before getting the value. +/// +#define AZAC_ERR_EMPTY_NULLABLE __AZAC_ERRCODE_FAILED(0x077) + +/// +/// The given model version string is not in the expected format. The format +/// is specified by the regular expression `^(latest|\d{4}-\d{2}-\d{2})(-preview)?$`. +/// +#define AZAC_ERR_INVALID_MODEL_VERSION_FORMAT __AZAC_ERRCODE_FAILED(0x078) + +/// +/// Malformed network message +/// +#define AZAC_ERR_NETWORK_MALFORMED __AZAC_ERRCODE_FAILED(0x090) + +/// +/// Unexpected message received +/// +#define AZAC_ERR_NETWORK_PROTOCOL_VIOLATION __AZAC_ERRCODE_FAILED(0x091) diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c.h new file mode 100644 index 0000000..61a4a5b --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c.h @@ -0,0 +1,51 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c.h: Master include header for public C API declarations +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_audio_config.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_audio_config.h new file mode 100644 index 0000000..a8d042e --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_audio_config.h @@ -0,0 +1,27 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_audio_config.h: Public API declarations for audio configuration related C methods and types +// + +#pragma once +#include + + +SPXAPI_(bool) audio_config_is_handle_valid(SPXAUDIOCONFIGHANDLE haudioConfig); +SPXAPI audio_config_create_audio_input_from_default_microphone(SPXAUDIOCONFIGHANDLE* haudioConfig); +SPXAPI audio_config_create_audio_input_from_a_microphone(SPXAUDIOCONFIGHANDLE* haudioConfig, const char* deviceName); +SPXAPI audio_config_create_audio_input_from_wav_file_name(SPXAUDIOCONFIGHANDLE* haudioConfig, const char* fileName); +SPXAPI audio_config_create_audio_input_from_stream(SPXAUDIOCONFIGHANDLE* haudioConfig, SPXAUDIOSTREAMHANDLE haudioStream); +SPXAPI audio_config_create_push_audio_input_stream(SPXAUDIOCONFIGHANDLE* haudioConfig, SPXAUDIOSTREAMHANDLE* haudioStream, SPXAUDIOSTREAMFORMATHANDLE hformat); +SPXAPI audio_config_create_pull_audio_input_stream(SPXAUDIOCONFIGHANDLE* haudioConfig, SPXAUDIOSTREAMHANDLE* haudioStream, SPXAUDIOSTREAMFORMATHANDLE hformat); +SPXAPI audio_config_create_audio_output_from_default_speaker(SPXAUDIOCONFIGHANDLE* haudioConfig); +SPXAPI audio_config_create_audio_output_from_a_speaker(SPXAUDIOCONFIGHANDLE* haudioConfig, const char* deviceName); +SPXAPI audio_config_create_audio_output_from_wav_file_name(SPXAUDIOCONFIGHANDLE* haudioConfig, const char* fileName); +SPXAPI audio_config_create_audio_output_from_stream(SPXAUDIOCONFIGHANDLE* haudioConfig, SPXAUDIOSTREAMHANDLE haudioStream); +SPXAPI audio_config_set_audio_processing_options(SPXAUDIOCONFIGHANDLE haudioConfig, SPXAUDIOPROCESSINGOPTIONSHANDLE haudioProcessingOptions); +SPXAPI audio_config_get_audio_processing_options(SPXAUDIOCONFIGHANDLE haudioConfig, SPXAUDIOPROCESSINGOPTIONSHANDLE* haudioProcessingOptions); +SPXAPI audio_config_release(SPXAUDIOCONFIGHANDLE haudioConfig); +SPXAPI audio_config_get_property_bag(SPXAUDIOCONFIGHANDLE haudioConfig, SPXPROPERTYBAGHANDLE* hpropbag); + diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_audio_processing_options.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_audio_processing_options.h new file mode 100644 index 0000000..ece9933 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_audio_processing_options.h @@ -0,0 +1,173 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_audio_processing_options.h: Public API declarations for audio processing options related C methods and types +// + +#pragma once +#include + +/// +/// Types of preset microphone array geometries. +/// See [Microphone Array Recommendations](/azure/cognitive-services/speech-service/speech-devices-sdk-microphone) for more details. +/// +typedef enum +{ + /// + /// Indicates that no geometry specified. Speech SDK will determine the microphone array geometry. + /// + AudioProcessingOptions_PresetMicrophoneArrayGeometry_Uninitialized, + /// + /// Indicates a microphone array with one microphone in the center and six microphones evenly spaced + /// in a circle with radius approximately equal to 42.5 mm. + /// + AudioProcessingOptions_PresetMicrophoneArrayGeometry_Circular7, + /// + /// Indicates a microphone array with one microphone in the center and three microphones evenly spaced + /// in a circle with radius approximately equal to 42.5 mm. + /// + AudioProcessingOptions_PresetMicrophoneArrayGeometry_Circular4, + /// + /// Indicates a microphone array with four linearly placed microphones with 40 mm spacing between them. + /// + AudioProcessingOptions_PresetMicrophoneArrayGeometry_Linear4, + /// + /// Indicates a microphone array with two linearly placed microphones with 40 mm spacing between them. + /// + AudioProcessingOptions_PresetMicrophoneArrayGeometry_Linear2, + /// + /// Indicates a microphone array with a single microphone. + /// + AudioProcessingOptions_PresetMicrophoneArrayGeometry_Mono, + /// + /// Indicates a microphone array with custom geometry. + /// + AudioProcessingOptions_PresetMicrophoneArrayGeometry_Custom +} AudioProcessingOptions_PresetMicrophoneArrayGeometry; + +/// +/// Types of microphone arrays. +/// +typedef enum +{ + AudioProcessingOptions_MicrophoneArrayType_Linear, + AudioProcessingOptions_MicrophoneArrayType_Planar +} AudioProcessingOptions_MicrophoneArrayType; + +/// +/// Defines speaker reference channel position in input audio. +/// +typedef enum +{ + /// + /// Indicates that the input audio does not have a speaker reference channel. + /// + AudioProcessingOptions_SpeakerReferenceChannel_None, + /// + /// Indicates that the last channel in the input audio corresponds to the speaker + /// reference for echo cancellation. + /// + AudioProcessingOptions_SpeakerReferenceChannel_LastChannel +} AudioProcessingOptions_SpeakerReferenceChannel; + +#pragma pack(push, 1) + +/// +/// Represents coordinates of a microphone. +/// +typedef struct +{ + /// + /// X-coordinate of the microphone in millimeters. + /// + int X; + /// + /// Y-coordinate of the microphone in millimeters. + /// + int Y; + /// + /// Z-coordinate of the microphone in millimeters. + /// + int Z; +} AudioProcessingOptions_MicrophoneCoordinates; + +/// +/// Represents the geometry of a microphone array. +/// +typedef struct +{ + /// + /// Type of microphone array. + /// + AudioProcessingOptions_MicrophoneArrayType microphoneArrayType; + /// + /// Start angle for beamforming in degrees. + /// + uint16_t beamformingStartAngle; + /// + /// End angle for beamforming in degrees. + /// + uint16_t beamformingEndAngle; + /// + /// Number of microphones in the microphone array. + /// + uint16_t numberOfMicrophones; + /// + /// Coordinates of microphones in the microphone array. + /// + AudioProcessingOptions_MicrophoneCoordinates* microphoneCoordinates; +} AudioProcessingOptions_MicrophoneArrayGeometry; + +#pragma pack(pop) + +/// +/// Disables built-in input audio processing. +/// +const int AUDIO_INPUT_PROCESSING_NONE = 0x00000000; +/// +/// Enables default built-in input audio processing. +/// +const int AUDIO_INPUT_PROCESSING_ENABLE_DEFAULT = 0x00000001; +/// +/// Disables dereverberation in the default audio processing pipeline. +/// +const int AUDIO_INPUT_PROCESSING_DISABLE_DEREVERBERATION = 0x00000002; +/// +/// Disables noise suppression in the default audio processing pipeline. +/// +const int AUDIO_INPUT_PROCESSING_DISABLE_NOISE_SUPPRESSION = 0x00000004; +/// +/// Disables automatic gain control in the default audio processing pipeline. +/// +const int AUDIO_INPUT_PROCESSING_DISABLE_GAIN_CONTROL = 0x00000008; +/// +/// Disables echo cancellation in the default audio processing pipeline. +/// +const int AUDIO_INPUT_PROCESSING_DISABLE_ECHO_CANCELLATION = 0x00000010; +/// +/// Enables voice activity detection in input audio processing. +/// +const int AUDIO_INPUT_PROCESSING_ENABLE_VOICE_ACTIVITY_DETECTION = 0x00000020; +/// +/// Enables the new version (V2) of input audio processing with improved echo cancellation performance. +/// This flag is mutually exclusive with AUDIO_INPUT_PROCESSING_ENABLE_DEFAULT flag. +/// AUDIO_INPUT_PROCESSING_DISABLE_* flags do not affect this pipeline. +/// This feature is currently in preview and only available for Windows x64 and ARM64 platform. +/// +const int AUDIO_INPUT_PROCESSING_ENABLE_V2 = 0x00000040; + +SPXAPI_(bool) audio_processing_options_is_handle_valid(SPXAUDIOPROCESSINGOPTIONSHANDLE hoptions); +SPXAPI audio_processing_options_create(SPXAUDIOPROCESSINGOPTIONSHANDLE* hoptions, int audioProcessingFlags); +SPXAPI audio_processing_options_create_from_preset_microphone_array_geometry(SPXAUDIOPROCESSINGOPTIONSHANDLE* hoptions, int audioProcessingFlags, AudioProcessingOptions_PresetMicrophoneArrayGeometry microphoneArrayGeometry, AudioProcessingOptions_SpeakerReferenceChannel speakerReferenceChannel); +SPXAPI audio_processing_options_create_from_microphone_array_geometry(SPXAUDIOPROCESSINGOPTIONSHANDLE* hoptions, int audioProcessingFlags, const AudioProcessingOptions_MicrophoneArrayGeometry* microphoneArrayGeometry, AudioProcessingOptions_SpeakerReferenceChannel speakerReferenceChannel); +SPXAPI audio_processing_options_get_audio_processing_flags(SPXAUDIOPROCESSINGOPTIONSHANDLE hoptions, int* audioProcessingFlags); +SPXAPI audio_processing_options_get_preset_microphone_array_geometry(SPXAUDIOPROCESSINGOPTIONSHANDLE hoptions, AudioProcessingOptions_PresetMicrophoneArrayGeometry* microphoneArrayGeometry); +SPXAPI audio_processing_options_get_microphone_array_type(SPXAUDIOPROCESSINGOPTIONSHANDLE hoptions, AudioProcessingOptions_MicrophoneArrayType* microphoneArrayType); +SPXAPI audio_processing_options_get_beamforming_start_angle(SPXAUDIOPROCESSINGOPTIONSHANDLE hoptions, uint16_t* startAngle); +SPXAPI audio_processing_options_get_beamforming_end_angle(SPXAUDIOPROCESSINGOPTIONSHANDLE hoptions, uint16_t* endAngle); +SPXAPI audio_processing_options_get_microphone_count(SPXAUDIOPROCESSINGOPTIONSHANDLE hoptions, uint16_t* microphoneCount); +SPXAPI audio_processing_options_get_microphone_coordinates(SPXAUDIOPROCESSINGOPTIONSHANDLE hoptions, AudioProcessingOptions_MicrophoneCoordinates* microphoneCoordinates, uint16_t microphoneCount); +SPXAPI audio_processing_options_get_speaker_reference_channel(SPXAUDIOPROCESSINGOPTIONSHANDLE hoptions, AudioProcessingOptions_SpeakerReferenceChannel* speakerReferenceChannel); +SPXAPI audio_processing_options_release(SPXAUDIOPROCESSINGOPTIONSHANDLE hoptions); +SPXAPI audio_processing_options_get_property_bag(SPXAUDIOPROCESSINGOPTIONSHANDLE hoptions, SPXPROPERTYBAGHANDLE* hpropbag); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_audio_stream.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_audio_stream.h new file mode 100644 index 0000000..0056480 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_audio_stream.h @@ -0,0 +1,67 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_audio_stream.h: Public API declarations for audio stream related C methods and types +// + +#pragma once +#include +#include + +typedef enum +{ + StreamStatus_Unknown = 0, + StreamStatus_NoData = 1, + StreamStatus_PartialData = 2, + StreamStatus_AllData = 3, + StreamStatus_Canceled = 4 +} Stream_Status; + +// audio_stream +SPXAPI_(bool) audio_stream_is_handle_valid(SPXAUDIOSTREAMHANDLE haudioStream); +SPXAPI audio_stream_create_push_audio_input_stream(SPXAUDIOSTREAMHANDLE* haudioStream, SPXAUDIOSTREAMFORMATHANDLE hformat); +SPXAPI audio_stream_create_pull_audio_input_stream(SPXAUDIOSTREAMHANDLE* haudioStream, SPXAUDIOSTREAMFORMATHANDLE hformat); +SPXAPI audio_stream_create_pull_audio_output_stream(SPXAUDIOSTREAMHANDLE* haudioStream); +SPXAPI audio_stream_create_push_audio_output_stream(SPXAUDIOSTREAMHANDLE* haudioStream); +SPXAPI audio_stream_release(SPXAUDIOSTREAMHANDLE haudioStream); + +// pull_audio_input_stream +typedef int (*CUSTOM_AUDIO_PULL_STREAM_READ_CALLBACK)(void* pvContext, uint8_t* buffer, uint32_t size); +typedef void (*CUSTOM_AUDIO_PULL_STREAM_CLOSE_CALLBACK)(void* pvContext); +typedef void (*CUSTOM_AUDIO_PULL_STREAM_GET_PROPERTY_CALLBACK)(void* pvContext, int id, uint8_t* value, uint32_t size); +SPXAPI pull_audio_input_stream_set_callbacks(SPXAUDIOSTREAMHANDLE haudioStream, void* pvContext, CUSTOM_AUDIO_PULL_STREAM_READ_CALLBACK readCallback, CUSTOM_AUDIO_PULL_STREAM_CLOSE_CALLBACK closeCallback); +SPXAPI pull_audio_input_stream_set_getproperty_callback(SPXAUDIOSTREAMHANDLE haudioStream, void* pvContext, CUSTOM_AUDIO_PULL_STREAM_GET_PROPERTY_CALLBACK getPropertyCallback); + +// push_audio_input_stream +SPXAPI push_audio_input_stream_write(SPXAUDIOSTREAMHANDLE haudioStream, uint8_t* buffer, uint32_t size); +SPXAPI push_audio_input_stream_close(SPXAUDIOSTREAMHANDLE haudioStream); +SPXAPI push_audio_input_stream_set_property_by_id(SPXAUDIOSTREAMHANDLE haudioStream, int id, const char* value); +SPXAPI push_audio_input_stream_set_property_by_name(SPXAUDIOSTREAMHANDLE haudioStream, const char* name, const char* value); + +// pull audio output stream +SPXAPI pull_audio_output_stream_read(SPXAUDIOSTREAMHANDLE haudioStream, uint8_t* buffer, uint32_t bufferSize, uint32_t* pfilledSize); + +// push_audio_output_stream +typedef int(*CUSTOM_AUDIO_PUSH_STREAM_WRITE_CALLBACK)(void* pvContext, uint8_t* buffer, uint32_t size); +typedef void(*CUSTOM_AUDIO_PUSH_STREAM_CLOSE_CALLBACK)(void* pvContext); +SPXAPI push_audio_output_stream_set_callbacks(SPXAUDIOSTREAMHANDLE haudioStream, void* pvContext, CUSTOM_AUDIO_PUSH_STREAM_WRITE_CALLBACK writeCallback, CUSTOM_AUDIO_PUSH_STREAM_CLOSE_CALLBACK closeCallback); + +// audio data stream +SPXAPI_(bool) audio_data_stream_is_handle_valid(SPXAUDIOSTREAMHANDLE haudioStream); +SPXAPI audio_data_stream_create_from_file(SPXAUDIOSTREAMHANDLE* haudioStream, const char* fileName); +SPXAPI audio_data_stream_create_from_result(SPXAUDIOSTREAMHANDLE* haudioStream, SPXRESULTHANDLE hresult); +SPXAPI audio_data_stream_create_from_keyword_result(SPXAUDIOSTREAMHANDLE* audioStreamHandle, SPXRESULTHANDLE resultHandle); +SPXAPI audio_data_stream_get_status(SPXAUDIOSTREAMHANDLE haudioStream, Stream_Status* status); +SPXAPI audio_data_stream_get_reason_canceled(SPXAUDIOSTREAMHANDLE haudioStream, Result_CancellationReason* reason); +SPXAPI audio_data_stream_get_canceled_error_code(SPXAUDIOSTREAMHANDLE haudioStream, Result_CancellationErrorCode* errorCode); +SPXAPI_(bool) audio_data_stream_can_read_data(SPXAUDIOSTREAMHANDLE haudioStream, uint32_t requestedSize); +SPXAPI_(bool) audio_data_stream_can_read_data_from_position(SPXAUDIOSTREAMHANDLE haudioStream, uint32_t requestedSize, uint32_t position); +SPXAPI audio_data_stream_read(SPXAUDIOSTREAMHANDLE haudioStream, uint8_t* buffer, uint32_t bufferSize, uint32_t* pfilledSize); +SPXAPI audio_data_stream_read_from_position(SPXAUDIOSTREAMHANDLE haudioStream, uint8_t* buffer, uint32_t bufferSize, uint32_t position, uint32_t* pfilledSize); +SPXAPI audio_data_stream_save_to_wave_file(SPXAUDIOSTREAMHANDLE haudioStream, const char* fileName); +SPXAPI audio_data_stream_get_position(SPXAUDIOSTREAMHANDLE haudioStream, uint32_t* position); +SPXAPI audio_data_stream_set_position(SPXAUDIOSTREAMHANDLE haudioStream, uint32_t position); +SPXAPI audio_data_stream_detach_input(SPXAUDIOSTREAMHANDLE audioStreamHandle); +SPXAPI audio_data_stream_get_property_bag(SPXAUDIOSTREAMHANDLE haudioStream, SPXPROPERTYBAGHANDLE* hpropbag); +SPXAPI audio_data_stream_release(SPXAUDIOSTREAMHANDLE haudioStream); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_audio_stream_format.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_audio_stream_format.h new file mode 100644 index 0000000..ac1e8e4 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_audio_stream_format.h @@ -0,0 +1,93 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_audio_stream_format.h: Public API declarations for audio stream format related C methods and types +// + +#pragma once +#include + +/// +/// Defines supported audio stream container format. +/// Changed in version 1.4.0. +/// +enum Audio_Stream_Container_Format +{ + /// + /// Stream ContainerFormat definition for OGG OPUS. + /// + StreamFormat_Ogg_Opus = 0x101, + + /// + /// Stream ContainerFormat definition for MP3. + /// + StreamFormat_Mp3 = 0x102, + + /// + /// Stream ContainerFormat definition for FLAC. Added in version 1.7.0. + /// + StreamFormat_Flac = 0x103, + + /// + /// Stream ContainerFormat definition for ALAW. Added in version 1.7.0. + /// + StreamFormat_Alaw = 0x104, + + /// + /// Stream ContainerFormat definition for MULAW. Added in version 1.7.0. + /// + StreamFormat_Mulaw = 0x105, + + /// + /// Stream ContainerFormat definition for AMRNB. Currently not supported. + /// + StreamFormat_Amrnb = 0x106, + + /// + /// Stream ContainerFormat definition for AMRWB. Currently not supported. + /// + StreamFormat_Amrwb = 0x107, + + /// + /// Stream ContainerFormat definition for any other or unknown format. + /// + StreamFormat_Any = 0x108, +}; + +/// +/// Defines supported audio stream wave format in WAV container. +/// +enum Audio_Stream_Wave_Format +{ + /// + /// Stream WaveFormat definition for PCM (pulse-code modulated) data in integer format. + /// + StreamWaveFormat_PCM = 0x0001, + + /// + /// Stream WaveFormat definition for A-law-encoded format. + /// + StreamWaveFormat_ALAW = 0x0006, + + /// + /// Stream WaveFormat definition for Mu-law-encoded format. + /// + StreamWaveFormat_MULAW = 0x0007, + + /// + /// Stream WaveFormat definition for G.722-encoded format. + /// + StreamWaveFormat_G722 = 0x028F +}; + +typedef enum Audio_Stream_Container_Format Audio_Stream_Container_Format; +typedef enum Audio_Stream_Wave_Format Audio_Stream_Wave_Format; + +SPXAPI_(bool) audio_stream_format_is_handle_valid(SPXAUDIOSTREAMFORMATHANDLE hformat); +SPXAPI audio_stream_format_create_from_default_input(SPXAUDIOSTREAMFORMATHANDLE* hformat); +SPXAPI audio_stream_format_create_from_waveformat(SPXAUDIOSTREAMFORMATHANDLE* hformat, uint32_t samplesPerSecond, uint8_t bitsPerSample, uint8_t channels, Audio_Stream_Wave_Format waveFormat); +SPXAPI audio_stream_format_create_from_waveformat_pcm(SPXAUDIOSTREAMFORMATHANDLE* hformat, uint32_t samplesPerSecond, uint8_t bitsPerSample, uint8_t channels); +SPXAPI audio_stream_format_create_from_default_output(SPXAUDIOSTREAMFORMATHANDLE* hformat); +SPXAPI audio_stream_format_create_from_compressed_format(SPXAUDIOSTREAMFORMATHANDLE* hformat, Audio_Stream_Container_Format compressedFormat); +SPXAPI audio_stream_format_release(SPXAUDIOSTREAMFORMATHANDLE hformat); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_auto_detect_source_lang_config.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_auto_detect_source_lang_config.h new file mode 100644 index 0000000..f62c5eb --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_auto_detect_source_lang_config.h @@ -0,0 +1,15 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include + +SPXAPI create_auto_detect_source_lang_config_from_open_range(SPXAUTODETECTSOURCELANGCONFIGHANDLE* hAutoDetectSourceLanguageconfig); +SPXAPI create_auto_detect_source_lang_config_from_languages(SPXAUTODETECTSOURCELANGCONFIGHANDLE* hAutoDetectSourceLanguageconfig, const char* languages); +SPXAPI create_auto_detect_source_lang_config_from_source_lang_config(SPXAUTODETECTSOURCELANGCONFIGHANDLE* hAutoDetectSourceLanguageconfig, SPXSOURCELANGCONFIGHANDLE hSourceLanguageConfig); +SPXAPI add_source_lang_config_to_auto_detect_source_lang_config(SPXAUTODETECTSOURCELANGCONFIGHANDLE hAutoDetectSourceLanguageconfig, SPXSOURCELANGCONFIGHANDLE hSourceLanguageConfig); +SPXAPI_(bool) auto_detect_source_lang_config_is_handle_valid(SPXAUTODETECTSOURCELANGCONFIGHANDLE hAutoDetectSourceLanguageconfig); +SPXAPI auto_detect_source_lang_config_release(SPXAUTODETECTSOURCELANGCONFIGHANDLE hAutoDetectSourceLanguageconfig); +SPXAPI auto_detect_source_lang_config_get_property_bag(SPXAUTODETECTSOURCELANGCONFIGHANDLE hAutoDetectSourceLanguageconfig, SPXPROPERTYBAGHANDLE* hpropbag); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_common.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_common.h new file mode 100644 index 0000000..ebfb802 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_common.h @@ -0,0 +1,81 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_common.h: Public API declarations for global C definitions and typedefs +// + +#pragma once + +#include +#include // must include after spxdebug.h or speechapi*.h (can NOT be included before) +#include +#include + +#define SPX_EXTERN_C AZAC_EXTERN_C +#ifndef SPXAPI_EXPORT +#define SPXAPI_EXPORT AZAC_API_EXPORT +#endif + +#define SPXAPI_NOTHROW AZAC_API_NOTHROW +#define SPXAPI_RESULTTYPE SPXHR +#define SPXAPI_CALLTYPE AZAC_API_CALLTYPE +#define SPXAPI_VCALLTYPE AZAC_VCALLTYPE + +#define SPXDLL_EXPORT AZAC_DLL_EXPORT + +#define SPXAPI SPX_EXTERN_C SPXAPI_EXPORT SPXAPI_RESULTTYPE SPXAPI_NOTHROW SPXAPI_CALLTYPE +#define SPXAPI_(type) SPX_EXTERN_C SPXAPI_EXPORT type SPXAPI_NOTHROW SPXAPI_CALLTYPE +#define SPXAPI__(type) SPX_EXTERN_C SPXAPI_EXPORT SPXAPI_NOTHROW type SPXAPI_CALLTYPE + +#define SPXAPIV SPX_EXTERN_C SPXAPI_EXPORT SPXAPI_NOTHROW SPXAPI_RESULTTYPE SPXAPI_VCALLTYPE +#define SPXAPIV_(type) SPX_EXTERN_C SPXAPI_EXPORT SPXAPI_NOTHROW type SPXAPI_VCALLTYPE + +#define SPXAPI_PRIVATE SPX_EXTERN_C SPXAPI_RESULTTYPE SPXAPI_NOTHROW SPXAPI_CALLTYPE +#define SPXAPI_PRIVATE_(type) SPX_EXTERN_C type SPXAPI_NOTHROW SPXAPI_CALLTYPE + +#define _spx_empty _azac_empty +#define _spxhandle _azac_handle +#define SPXHANDLE AZAC_HANDLE +#define SPXERRORHANDLE AZAC_HANDLE + +#define SPXPROPERTYBAGHANDLE AZAC_HANDLE +typedef SPXHANDLE SPXASYNCHANDLE; +typedef SPXHANDLE SPXFACTORYHANDLE; +typedef SPXHANDLE SPXRECOHANDLE; +typedef SPXHANDLE SPXSYNTHHANDLE; +typedef SPXHANDLE SPXRESULTHANDLE; +typedef SPXHANDLE SPXEVENTHANDLE; +typedef SPXHANDLE SPXSESSIONHANDLE; +typedef SPXHANDLE SPXTRIGGERHANDLE; +typedef SPXHANDLE SPXLUMODELHANDLE; +typedef SPXHANDLE SPXKEYWORDHANDLE; +typedef SPXHANDLE SPXAUDIOSTREAMFORMATHANDLE; +typedef SPXHANDLE SPXAUDIOSTREAMHANDLE; +typedef SPXHANDLE SPXAUDIOCONFIGHANDLE; +typedef SPXHANDLE SPXSPEECHCONFIGHANDLE; +typedef SPXHANDLE SPXCONNECTIONHANDLE; +typedef SPXHANDLE SPXCONNECTIONMESSAGEHANDLE; +typedef SPXHANDLE SPXACTIVITYHANDLE; +typedef SPXHANDLE SPXACTIVITYJSONHANDLE; +typedef SPXHANDLE SPXGRAMMARHANDLE; +typedef SPXHANDLE SPXPHRASEHANDLE; +typedef SPXHANDLE SPXUSERHANDLE; +typedef SPXHANDLE SPXPARTICIPANTHANDLE; +typedef SPXHANDLE SPXAUTODETECTSOURCELANGCONFIGHANDLE; +typedef SPXHANDLE SPXSOURCELANGCONFIGHANDLE; +typedef SPXHANDLE SPXCONVERSATIONHANDLE; +typedef SPXHANDLE SPXMEETINGHANDLE; +typedef SPXHANDLE SPXCONVERSATIONTRANSLATORHANDLE; +typedef SPXHANDLE SPXVOICEPROFILECLIENTHANDLE; +typedef SPXHANDLE SPXVOICEPROFILEHANDLE; +typedef SPXHANDLE SPXSPEAKERIDHANDLE; +typedef SPXHANDLE SPXSIMODELHANDLE; +typedef SPXHANDLE SPXSVMODELHANDLE; +typedef SPXHANDLE SPXPRONUNCIATIONASSESSMENTCONFIGHANDLE; +typedef SPXHANDLE SPXAUDIOPROCESSINGOPTIONSHANDLE; +typedef SPXHANDLE SPXSPEECHRECOMODELHANDLE; +typedef SPXHANDLE SPXREQUESTHANDLE; + +#define SPXHANDLE_INVALID ((SPXHANDLE)-1) +#define SPXHANDLE_RESERVED1 ((SPXHANDLE)+1) diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_connection.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_connection.h new file mode 100644 index 0000000..3bb69c9 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_connection.h @@ -0,0 +1,46 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include + +SPXAPI connection_from_recognizer(SPXRECOHANDLE recognizerHandle, SPXCONNECTIONHANDLE* connectionHandle); +SPXAPI connection_from_conversation_translator(SPXCONVERSATIONTRANSLATORHANDLE convTransHandle, SPXCONNECTIONHANDLE* connectionHandle); +SPXAPI connection_from_dialog_service_connector(SPXRECOHANDLE convTransHandle, SPXCONNECTIONHANDLE* connectionHandle); +SPXAPI connection_from_speech_synthesizer(SPXSYNTHHANDLE synthesizerHandle, SPXCONNECTIONHANDLE* connectionHandle); + +SPXAPI_(bool) connection_handle_is_valid(SPXCONNECTIONHANDLE handle); +SPXAPI connection_handle_release(SPXCONNECTIONHANDLE handle); +SPXAPI connection_async_handle_release(SPXASYNCHANDLE hasync); + +SPXAPI connection_open(SPXCONNECTIONHANDLE handle, bool forContinuousRecognition); +SPXAPI connection_close(SPXCONNECTIONHANDLE handle); +SPXAPI connection_set_message_property(SPXCONNECTIONHANDLE handle, const char* path, const char* name, const char* value); +SPXAPI connection_send_message(SPXCONNECTIONHANDLE handle, const char* path, const char* payload); +SPXAPI connection_send_message_async(SPXCONNECTIONHANDLE handle, const char* path, const char* payload, SPXASYNCHANDLE* phasync); + +SPXAPI connection_send_message_data(SPXCONNECTIONHANDLE handle, const char* path, uint8_t* data, uint32_t size); +SPXAPI connection_send_message_data_async(SPXCONNECTIONHANDLE handle, const char* path, uint8_t* data, uint32_t size, SPXASYNCHANDLE* phasync); + +SPXAPI connection_send_message_wait_for(SPXASYNCHANDLE hasync, uint32_t milliseconds); + +SPXAPI connection_get_property_bag(SPXRECOHANDLE hconn, SPXPROPERTYBAGHANDLE* hpropbag); + +typedef void(*CONNECTION_CALLBACK_FUNC)(SPXEVENTHANDLE event, void* context); +SPXAPI connection_connected_set_callback(SPXCONNECTIONHANDLE connection, CONNECTION_CALLBACK_FUNC callback, void* context); +SPXAPI connection_disconnected_set_callback(SPXCONNECTIONHANDLE connection, CONNECTION_CALLBACK_FUNC callback, void* context); +SPXAPI connection_message_received_set_callback(SPXCONNECTIONHANDLE connection, CONNECTION_CALLBACK_FUNC callback, void* context); + +SPXAPI_(bool) connection_message_received_event_handle_is_valid(SPXEVENTHANDLE hevent); +SPXAPI connection_message_received_event_handle_release(SPXEVENTHANDLE hevent); + +SPXAPI connection_message_received_event_get_message(SPXEVENTHANDLE hevent, SPXCONNECTIONMESSAGEHANDLE* hcm); + +SPXAPI_(bool) connection_message_handle_is_valid(SPXCONNECTIONMESSAGEHANDLE handle); +SPXAPI connection_message_handle_release(SPXCONNECTIONMESSAGEHANDLE handle); + +SPXAPI connection_message_get_property_bag(SPXCONNECTIONMESSAGEHANDLE hcm, SPXPROPERTYBAGHANDLE* hpropbag); +SPXAPI connection_message_get_data(SPXCONNECTIONMESSAGEHANDLE hcm, uint8_t* data, uint32_t size); +SPXAPI_(uint32_t) connection_message_get_data_size(SPXCONNECTIONMESSAGEHANDLE hcm); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_conversation.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_conversation.h new file mode 100644 index 0000000..be12f34 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_conversation.h @@ -0,0 +1,28 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_conversation.h: Public API declarations for conversation related C methods and typedefs +// + +#pragma once +#include + +SPXAPI conversation_create_from_config(SPXCONVERSATIONHANDLE* phconv, SPXSPEECHCONFIGHANDLE hspeechconfig, const char* id); +SPXAPI conversation_update_participant_by_user_id(SPXCONVERSATIONHANDLE hconv, bool add, const char* userId); +SPXAPI conversation_update_participant_by_user(SPXCONVERSATIONHANDLE hconv, bool add, SPXUSERHANDLE huser); +SPXAPI conversation_update_participant(SPXCONVERSATIONHANDLE hconv, bool add, SPXPARTICIPANTHANDLE hparticipant); +SPXAPI conversation_get_conversation_id(SPXCONVERSATIONHANDLE hconv, char* id, size_t size); +SPXAPI conversation_end_conversation(SPXCONVERSATIONHANDLE hconv); +SPXAPI conversation_get_property_bag(SPXCONVERSATIONHANDLE hconv, SPXPROPERTYBAGHANDLE* phpropbag); +SPXAPI conversation_release_handle(SPXHANDLE handle); + +SPXAPI conversation_start_conversation(SPXCONVERSATIONHANDLE hconv); +SPXAPI conversation_delete_conversation(SPXCONVERSATIONHANDLE hconv); +SPXAPI conversation_lock_conversation(SPXCONVERSATIONHANDLE hconv); +SPXAPI conversation_unlock_conversation(SPXCONVERSATIONHANDLE hconv); +SPXAPI conversation_mute_all_participants(SPXCONVERSATIONHANDLE hconv); +SPXAPI conversation_unmute_all_participants(SPXCONVERSATIONHANDLE hconv); +SPXAPI conversation_mute_participant(SPXCONVERSATIONHANDLE hconv, const char * participantId); +SPXAPI conversation_unmute_participant(SPXCONVERSATIONHANDLE hconv, const char * participantId); + diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_conversation_transcription_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_conversation_transcription_result.h new file mode 100644 index 0000000..7bcc7b1 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_conversation_transcription_result.h @@ -0,0 +1,11 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_conversation_transcriber_result.h: Public API declarations for ConversationTranscriberResult related C methods and enumerations +// + +#pragma once +#include + +SPXAPI conversation_transcription_result_get_speaker_id(SPXRESULTHANDLE hresult, char* pszSpeakerId, uint32_t cchSpeakerId); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_conversation_translator.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_conversation_translator.h new file mode 100644 index 0000000..0b1881b --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_conversation_translator.h @@ -0,0 +1,63 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_conversation_translator.h: Public API declarations for conversation translator related C methods and typedefs +// + +#pragma once +#include +#include + +#ifdef __cplusplus +#include +typedef Microsoft::CognitiveServices::Speech::Transcription::ParticipantChangedReason ParticipantChangedReason; +#else +#include +#endif + +typedef void(*PCONV_TRANS_CALLBACK)(SPXCONVERSATIONTRANSLATORHANDLE hConvTrans, SPXEVENTHANDLE hEvent, void* pvContext); + +SPXAPI conversation_translator_create_from_config(SPXCONVERSATIONTRANSLATORHANDLE* phandle, SPXAUDIOCONFIGHANDLE haudioinput); +SPXAPI conversation_translator_get_property_bag(SPXCONVERSATIONTRANSLATORHANDLE hconvtranslator, SPXPROPERTYBAGHANDLE* phpropertyBag); + +SPXAPI conversation_translator_join(SPXCONVERSATIONTRANSLATORHANDLE hconvtranslator, SPXCONVERSATIONHANDLE hconv, const char* psznickname); +SPXAPI conversation_translator_join_with_id(SPXCONVERSATIONTRANSLATORHANDLE hconvtranslator, const char *pszconversationid, const char* psznickname, const char * pszlang); +SPXAPI conversation_translator_start_transcribing(SPXCONVERSATIONTRANSLATORHANDLE hconvtranslator); +SPXAPI conversation_translator_stop_transcribing(SPXCONVERSATIONTRANSLATORHANDLE hconvtranslator); +SPXAPI conversation_translator_send_text_message(SPXCONVERSATIONTRANSLATORHANDLE hconvtranslator, const char *pszmessage); +SPXAPI conversation_translator_leave(SPXCONVERSATIONTRANSLATORHANDLE hconvtranslator); +SPXAPI conversation_translator_set_authorization_token(SPXCONVERSATIONTRANSLATORHANDLE hconvtranslator, const char* pszAuthToken, const char* pszRegion); + +SPXAPI_(bool) conversation_translator_handle_is_valid(SPXCONVERSATIONTRANSLATORHANDLE handle); +SPXAPI conversation_translator_handle_release(SPXHANDLE handle); + +SPXAPI conversation_translator_session_started_set_callback(SPXCONVERSATIONTRANSLATORHANDLE hConvTrans, PCONV_TRANS_CALLBACK pCallback, void* pvContext); +SPXAPI conversation_translator_session_stopped_set_callback(SPXCONVERSATIONTRANSLATORHANDLE hConvTrans, PCONV_TRANS_CALLBACK pCallback, void* pvContext); +SPXAPI conversation_translator_canceled_set_callback(SPXCONVERSATIONTRANSLATORHANDLE hConvTrans, PCONV_TRANS_CALLBACK pCallback, void* pvContext); +SPXAPI conversation_translator_participants_changed_set_callback(SPXCONVERSATIONTRANSLATORHANDLE hConvTrans, PCONV_TRANS_CALLBACK pCallback, void* pvContext); +SPXAPI conversation_translator_conversation_expiration_set_callback(SPXCONVERSATIONTRANSLATORHANDLE hConvTrans, PCONV_TRANS_CALLBACK pCallback, void* pvContext); +SPXAPI conversation_translator_transcribing_set_callback(SPXCONVERSATIONTRANSLATORHANDLE hConvTrans, PCONV_TRANS_CALLBACK pCallback, void* pvContext); +SPXAPI conversation_translator_transcribed_set_callback(SPXCONVERSATIONTRANSLATORHANDLE hConvTrans, PCONV_TRANS_CALLBACK pCallback, void* pvContext); +SPXAPI conversation_translator_text_message_recevied_set_callback(SPXCONVERSATIONTRANSLATORHANDLE hConvTrans, PCONV_TRANS_CALLBACK pCallback, void* pvContext); + +SPXAPI conversation_translator_connection_connected_set_callback(SPXCONNECTIONHANDLE hConnection, CONNECTION_CALLBACK_FUNC pCallback, void * pvContext); +SPXAPI conversation_translator_connection_disconnected_set_callback(SPXCONNECTIONHANDLE hConnection, CONNECTION_CALLBACK_FUNC pCallback, void * pvContext); + +SPXAPI_(bool) conversation_translator_event_handle_is_valid(SPXCONVERSATIONTRANSLATORHANDLE handle); +SPXAPI conversation_translator_event_handle_release(SPXHANDLE handle); + +SPXAPI conversation_translator_event_get_expiration_time(SPXEVENTHANDLE hevent, int32_t* pexpirationminutes); +SPXAPI conversation_translator_event_get_participant_changed_reason(SPXEVENTHANDLE hevent, ParticipantChangedReason* preason); +SPXAPI conversation_translator_event_get_participant_changed_at_index(SPXEVENTHANDLE hevent, int index, SPXPARTICIPANTHANDLE* phparticipant); + +SPXAPI conversation_translator_result_get_user_id(SPXRESULTHANDLE hresult, char* pszUserId, uint32_t cchUserId); + +SPXAPI conversation_translator_result_get_original_lang(SPXRESULTHANDLE hresult, char * psz, uint32_t * pcch); + +SPXAPI conversation_translator_participant_get_avatar(SPXEVENTHANDLE hevent, char * psz, uint32_t * pcch); +SPXAPI conversation_translator_participant_get_displayname(SPXEVENTHANDLE hevent, char * psz, uint32_t * pcch); +SPXAPI conversation_translator_participant_get_id(SPXEVENTHANDLE hevent, char * psz, uint32_t * pcch); +SPXAPI conversation_translator_participant_get_is_muted(SPXEVENTHANDLE hevent, bool * pMuted); +SPXAPI conversation_translator_participant_get_is_host(SPXEVENTHANDLE hevent, bool * pIsHost); +SPXAPI conversation_translator_participant_get_is_using_tts(SPXEVENTHANDLE hevent, bool * ptts); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_diagnostics.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_diagnostics.h new file mode 100644 index 0000000..25b1a73 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_diagnostics.h @@ -0,0 +1,8 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include +#include // must include after spxdebug.h or speechapi*.h (can NOT be included before) diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_dialog_service_config.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_dialog_service_config.h new file mode 100644 index 0000000..4fb8b35 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_dialog_service_config.h @@ -0,0 +1,15 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_dialog_service_config.h: Public API declarations for dialog service connector configuration related C methods and types +// +#pragma once + +#include + +SPXAPI bot_framework_config_from_subscription(SPXSPEECHCONFIGHANDLE* ph_config, const char* subscription, const char* region, const char *bot_Id); +SPXAPI bot_framework_config_from_authorization_token(SPXSPEECHCONFIGHANDLE* ph_config, const char* auth_token, const char* region, const char* bot_Id); + +SPXAPI custom_commands_config_from_subscription(SPXSPEECHCONFIGHANDLE* ph_dialog_service_config, const char* app_id, const char *subscription, const char* region); +SPXAPI custom_commands_config_from_authorization_token(SPXSPEECHCONFIGHANDLE* ph_dialog_service_config, const char* app_id, const char *auth_token, const char* region); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_dialog_service_connector.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_dialog_service_connector.h new file mode 100644 index 0000000..94e0fbf --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_dialog_service_connector.h @@ -0,0 +1,92 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_dialog_service_connector.h: Public API declaration for Dialog Service Connector related C methods. +// + +#pragma once +#include + +SPXAPI_(bool) dialog_service_connector_handle_is_valid(SPXRECOHANDLE h_connector); +SPXAPI dialog_service_connector_handle_release(SPXRECOHANDLE h_connector); + +SPXAPI_(bool) dialog_service_connector_async_handle_is_valid(SPXASYNCHANDLE h_async); +SPXAPI dialog_service_connector_async_handle_release(SPXASYNCHANDLE h_async); + +SPXAPI_(bool) dialog_service_connector_async_void_handle_is_valid(SPXASYNCHANDLE h_async); +SPXAPI dialog_service_connector_async_void_handle_release(SPXASYNCHANDLE h_async); + +SPXAPI_(bool) dialog_service_connector_async_string_handle_is_valid(SPXASYNCHANDLE h_async); +SPXAPI dialog_service_connector_async_string_handle_release(SPXASYNCHANDLE h_async); + +SPXAPI_(bool) dialog_service_connector_async_reco_result_handle_is_valid(SPXASYNCHANDLE h_async); +SPXAPI dialog_service_connector_async_reco_result_handle_release(SPXASYNCHANDLE h_async); + +SPXAPI_(bool) dialog_service_connector_activity_received_event_handle_is_valid(SPXEVENTHANDLE h_event); +SPXAPI dialog_service_connector_activity_received_event_release(SPXEVENTHANDLE h_event); + +SPXAPI_(bool) dialog_service_connector_turn_status_received_handle_is_valid(SPXEVENTHANDLE h_event); +SPXAPI dialog_service_connector_turn_status_received_release(SPXEVENTHANDLE h_event); + +SPXAPI dialog_service_connector_get_property_bag(SPXRECOHANDLE h_connector, SPXPROPERTYBAGHANDLE* h_prop_bag); + +SPXAPI dialog_service_connector_connect(SPXRECOHANDLE h_connector); +SPXAPI dialog_service_connector_connect_async(SPXRECOHANDLE h_connector, SPXASYNCHANDLE* p_async); +SPXAPI dialog_service_connector_connect_async_wait_for(SPXASYNCHANDLE h_async, uint32_t milliseconds); + +SPXAPI dialog_service_connector_disconnect(SPXRECOHANDLE h_connector); +SPXAPI dialog_service_connector_disconnect_async(SPXRECOHANDLE h_connector, SPXASYNCHANDLE* p_async); +SPXAPI dialog_service_connector_disconnect_async_wait_for(SPXASYNCHANDLE h_async, uint32_t milliseconds); + +SPXAPI dialog_service_connector_send_activity(SPXRECOHANDLE h_connector, const char* activity, char* interaction_id); +SPXAPI dialog_service_connector_send_activity_async(SPXRECOHANDLE h_connector, const char* activity, SPXASYNCHANDLE* p_async); +SPXAPI dialog_service_connector_send_activity_async_wait_for(SPXASYNCHANDLE h_async, uint32_t milliseconds, char* interaction_id); + +SPXAPI dialog_service_connector_start_keyword_recognition(SPXRECOHANDLE h_connector, SPXKEYWORDHANDLE h_keyword); +SPXAPI dialog_service_connector_start_keyword_recognition_async(SPXRECOHANDLE h_connector, SPXKEYWORDHANDLE h_keyword, SPXASYNCHANDLE* p_async); +SPXAPI dialog_service_connector_start_keyword_recognition_async_wait_for(SPXASYNCHANDLE h_async, uint32_t milliseconds); + +SPXAPI dialog_service_connector_stop_keyword_recognition(SPXRECOHANDLE h_connector); +SPXAPI dialog_service_connector_stop_keyword_recognition_async(SPXRECOHANDLE h_connector, SPXASYNCHANDLE* p_async); +SPXAPI dialog_service_connector_stop_keyword_recognition_async_wait_for(SPXASYNCHANDLE h_async, uint32_t milliseconds); + +SPXAPI dialog_service_connector_listen_once(SPXRECOHANDLE h_connector, SPXRESULTHANDLE* p_result); +SPXAPI dialog_service_connector_listen_once_async(SPXRECOHANDLE h_connector, SPXASYNCHANDLE* p_async); +SPXAPI dialog_service_connector_listen_once_async_wait_for(SPXASYNCHANDLE h_async, uint32_t milliseconds, SPXRESULTHANDLE* p_result); + +SPXAPI dialog_service_connector_start_continuous_listening(SPXRECOHANDLE h_connector); +SPXAPI dialog_service_connector_start_continuous_listening_async(SPXRECOHANDLE h_connector, SPXASYNCHANDLE* p_async); + +SPXAPI dialog_service_connector_stop_listening(SPXRECOHANDLE h_connector); +SPXAPI dialog_service_connector_stop_listening_async(SPXRECOHANDLE h_connector, SPXASYNCHANDLE* p_async); + +typedef void(*PSESSION_CALLBACK_FUNC)(SPXRECOHANDLE h_connector, SPXEVENTHANDLE h_event, void* pv_context); + +SPXAPI dialog_service_connector_session_started_set_callback(SPXRECOHANDLE h_connector, PSESSION_CALLBACK_FUNC p_callback, void *pv_context); +SPXAPI dialog_service_connector_session_stopped_set_callback(SPXRECOHANDLE h_connector, PSESSION_CALLBACK_FUNC p_callback, void *pv_context); + +SPXAPI dialog_service_connector_speech_start_detected_set_callback(SPXRECOHANDLE h_connector, PSESSION_CALLBACK_FUNC p_callback, void* pv_context); +SPXAPI dialog_service_connector_speech_end_detected_set_callback(SPXRECOHANDLE h_connector, PSESSION_CALLBACK_FUNC p_callback, void* pv_context); + +typedef void(*PRECOGNITION_CALLBACK_FUNC)(SPXRECOHANDLE h_connector, SPXEVENTHANDLE h_event, void* pv_context); + +SPXAPI dialog_service_connector_recognized_set_callback(SPXRECOHANDLE h_connector, PRECOGNITION_CALLBACK_FUNC p_callback, void *pv_context); +SPXAPI dialog_service_connector_recognizing_set_callback(SPXRECOHANDLE h_connector, PRECOGNITION_CALLBACK_FUNC p_callback, void *pv_context); +SPXAPI dialog_service_connector_canceled_set_callback(SPXRECOHANDLE h_connector, PRECOGNITION_CALLBACK_FUNC p_callback, void *pv_context); +SPXAPI dialog_service_connector_activity_received_set_callback(SPXRECOHANDLE h_connector, PRECOGNITION_CALLBACK_FUNC p_callback, void *pv_context); +SPXAPI dialog_service_connector_turn_status_received_set_callback(SPXRECOHANDLE h_connector, PRECOGNITION_CALLBACK_FUNC p_callback, void* pv_context); + +SPXAPI dialog_service_connector_activity_received_event_get_activity_size(SPXEVENTHANDLE h_event, size_t* size); +SPXAPI dialog_service_connector_activity_received_event_get_activity(SPXEVENTHANDLE h_event, char* p_activity, size_t size); +SPXAPI_(bool) dialog_service_connector_activity_received_event_has_audio(SPXEVENTHANDLE h_event); +SPXAPI dialog_service_connector_activity_received_event_get_audio(SPXEVENTHANDLE h_event, SPXAUDIOSTREAMHANDLE* p_audio); + +SPXAPI dialog_service_connector_turn_status_received_get_interaction_id_size(SPXEVENTHANDLE h_event, size_t* size); +SPXAPI dialog_service_connector_turn_status_received_get_interaction_id(SPXEVENTHANDLE h_event, char* p_interaction_id, size_t size); +SPXAPI dialog_service_connector_turn_status_received_get_conversation_id_size(SPXEVENTHANDLE h_event, size_t* size); +SPXAPI dialog_service_connector_turn_status_received_get_conversation_id(SPXEVENTHANDLE h_event, char* p_interaction_id, size_t size); +SPXAPI dialog_service_connector_turn_status_received_get_status(SPXEVENTHANDLE h_event, int* p_status); + +SPXAPI dialog_service_connector_recognized_size(SPXEVENTHANDLE h_event, uint32_t* size); +SPXAPI dialog_service_connector_recognized_get_result(SPXEVENTHANDLE h_event, uint32_t* size); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_embedded_speech_config.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_embedded_speech_config.h new file mode 100644 index 0000000..fb45fc1 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_embedded_speech_config.h @@ -0,0 +1,21 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include +#include +#include +#include + +SPXAPI embedded_speech_config_create(SPXSPEECHCONFIGHANDLE* hconfig); +SPXAPI embedded_speech_config_add_path(SPXSPEECHCONFIGHANDLE hconfig, const char* path); +SPXAPI embedded_speech_config_get_num_speech_reco_models(SPXSPEECHCONFIGHANDLE hconfig, uint32_t* numModels); +SPXAPI embedded_speech_config_get_speech_reco_model(SPXSPEECHCONFIGHANDLE hconfig, uint32_t index, SPXSPEECHRECOMODELHANDLE* hmodel); +SPXAPI embedded_speech_config_get_num_speech_translation_models(SPXSPEECHCONFIGHANDLE hconfig, uint32_t* numModels); +SPXAPI embedded_speech_config_get_speech_translation_model(SPXSPEECHCONFIGHANDLE hconfig, uint32_t index, SPXSPEECHRECOMODELHANDLE* hmodel); +SPXAPI embedded_speech_config_set_speech_recognition_model(SPXSPEECHCONFIGHANDLE hconfig, const char* name, const char* license); +SPXAPI embedded_speech_config_set_speech_synthesis_voice(SPXSPEECHCONFIGHANDLE hconfig, const char* name, const char* license); +SPXAPI embedded_speech_config_set_speech_translation_model(SPXSPEECHCONFIGHANDLE hconfig, const char* name, const char* license); +SPXAPI embedded_speech_config_set_keyword_recognition_model(SPXSPEECHCONFIGHANDLE hconfig, const char* name, const char* license); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_error.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_error.h new file mode 100644 index 0000000..0a581e3 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_error.h @@ -0,0 +1,9 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// + +#pragma once +#include +#include // must include after spxdebug.h or speechapi*.h (can NOT be included before) diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_ext_audiocompression.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_ext_audiocompression.h new file mode 100644 index 0000000..8af2d78 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_ext_audiocompression.h @@ -0,0 +1,105 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include "speechapi_c_common.h" + +const char CODECCREATEEXPORTNAME[] = "codec_create"; +struct codec_c_interface; +typedef struct codec_c_interface* codec_c_interface_P; +typedef codec_c_interface_P SPXCODECCTYPE; + +/*! \cond INTERNAL */ + +/** + * The SPX_CODEC_CLIENT_GET_PROPERTY represents the function reading a property value + * @param id Property id. + * @param buffer caller provided buffer to receive the value of the property + * @param buffersize buffer size. If buffer is passed as null it will return the required buffer size. + * @param codecContext A pointer to caller data provided through the codec_create call. + * @return A return code or zero if successful. + */ +typedef SPXAPI_RESULTTYPE (SPXAPI_CALLTYPE *SPX_CODEC_CLIENT_GET_PROPERTY)(const char* id, char* buffer, uint64_t* buffersize, void* codecContext); + +/** + * The AUDIO_ENCODER_ONENCODEDDATA type represents an application-defined + * status callback function used to provide the encoded data. + * @param pBuffer audio data buffer. + * @param bytesToWrite The length of pBuffer in bytes. + * @param duration_100nanos The duration of the audio sample + * @param pContext A pointer to the application-defined callback context. + */ +typedef void(SPXAPI_CALLTYPE *AUDIO_ENCODER_ONENCODEDDATA)(const uint8_t* pBuffer, size_t bytesToWrite, uint64_t duration_100nanos, void* pContext); + +struct codec_c_interface +{ + /** + * @param codec codec Object returned by the codec_create call to be initialized + * @param inputSamplesPerSecond sample rate for the input audio + * @param inputBitsPerSample bits per sample for the input audio + * @param inputChannels number of channel of the input audio + * @param dataCallback An application defined callback. + * @param pContext A pointer to the application-defined callback context. + * @return A return code or zero if successful. + */ + SPXAPI_RESULTTYPE (SPXAPI_CALLTYPE *init)( + SPXCODECCTYPE codec, + uint32_t inputSamplesPerSecond, + uint8_t inputBitsPerSample, + uint8_t inputChannels, + AUDIO_ENCODER_ONENCODEDDATA datacallback, + void* pContext); + + /** + * @param codec codec object returned by the codec_create call. + * @param buffer caller provided buffer to receive the value of the property + * @param buffersize buffer size. If buffer is passed as null it will return the required buffer size. + * @return A return code or zero if successful. + */ + SPXAPI_RESULTTYPE (SPXAPI_CALLTYPE* get_format_type)(SPXCODECCTYPE codec, char* buffer, uint64_t* buffersize); + + /** + * Encodes raw PCM data. + * @param codec codec object returned by the codec_create call. + * @param pBuffer The PCM data. + * @param bytesToWrite The length pBuffer. + * @return A return code or zero if successful. + */ + SPXAPI_RESULTTYPE (SPXAPI_CALLTYPE *encode) (SPXCODECCTYPE codec, const uint8_t* pBuffer, size_t bytesToWrite); + + /** + * Flushes the encoder. + * @param codec codec object returned by the codec_create call. + * @return A return code or zero if successful. + */ + SPXAPI_RESULTTYPE(SPXAPI_CALLTYPE* flush)(SPXCODECCTYPE codec); + + /** + * Terminate the encoded stream immediately + * @param codec codec object returned by the codec_create call. + * @return A return code or zero if successful. + */ + SPXAPI_RESULTTYPE (SPXAPI_CALLTYPE *endstream)(SPXCODECCTYPE codec); + + /** + * Destroys the encoder. The codec object should not be used anymore after this call. + * @param codec codec object returned by the codec_create call. + * @return A return code or zero if successful. + */ + SPXAPI_RESULTTYPE (SPXAPI_CALLTYPE *destroy) (SPXCODECCTYPE codec); +}; + +/** +* Creates a codec object. This method needs to be exported from the dll +* @param codecid - codec id, can be null or empty if the library implements only one codec. +* @param codecContext - context to be used to call back to the caller +* @param property_read_func - function to read properties +* @return A codec object +*/ + +SPX_EXTERN_C SPXDLL_EXPORT SPXCODECCTYPE codec_create(const char* codecid, void* codecContext, SPX_CODEC_CLIENT_GET_PROPERTY property_read_func); +typedef SPXCODECCTYPE (*PCODEC_CREATE_FUNC)(const char* codecid, void* codecContext, SPX_CODEC_CLIENT_GET_PROPERTY property_read_func); + +/*! \endcond */ diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_factory.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_factory.h new file mode 100644 index 0000000..43e8b4b --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_factory.h @@ -0,0 +1,29 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include + +SPXAPI recognizer_create_speech_recognizer_from_config(SPXRECOHANDLE* phreco, SPXSPEECHCONFIGHANDLE hspeechconfig, SPXAUDIOCONFIGHANDLE haudioInput); +SPXAPI recognizer_create_speech_recognizer_from_auto_detect_source_lang_config(SPXRECOHANDLE* phreco, SPXSPEECHCONFIGHANDLE hspeechconfig, SPXAUTODETECTSOURCELANGCONFIGHANDLE hautoDetectSourceLangConfig, SPXAUDIOCONFIGHANDLE haudioInput); +SPXAPI recognizer_create_speech_recognizer_from_source_lang_config(SPXRECOHANDLE* phreco, SPXSPEECHCONFIGHANDLE hspeechconfig, SPXSOURCELANGCONFIGHANDLE hSourceLangConfig, SPXAUDIOCONFIGHANDLE haudioInput); +SPXAPI recognizer_create_translation_recognizer_from_config(SPXRECOHANDLE* phreco, SPXSPEECHCONFIGHANDLE hspeechconfig, SPXAUDIOCONFIGHANDLE haudioInput); +SPXAPI recognizer_create_translation_recognizer_from_auto_detect_source_lang_config(SPXRECOHANDLE* phreco, SPXSPEECHCONFIGHANDLE hspeechconfig, SPXAUTODETECTSOURCELANGCONFIGHANDLE hautoDetectSourceLangConfig, SPXAUDIOCONFIGHANDLE haudioInput); +SPXAPI recognizer_create_intent_recognizer_from_config(SPXRECOHANDLE* phreco, SPXSPEECHCONFIGHANDLE hspeechconfig, SPXAUDIOCONFIGHANDLE haudioInput); +SPXAPI recognizer_create_keyword_recognizer_from_audio_config(SPXRECOHANDLE* phreco, SPXAUDIOCONFIGHANDLE haudio); +SPXAPI recognizer_create_source_language_recognizer_from_auto_detect_source_lang_config(SPXRECOHANDLE* phreco, SPXSPEECHCONFIGHANDLE hspeechconfig, SPXAUTODETECTSOURCELANGCONFIGHANDLE hautoDetectSourceLangConfig, SPXAUDIOCONFIGHANDLE haudioInput); +SPXAPI synthesizer_create_speech_synthesizer_from_config(SPXSYNTHHANDLE* phsynth, SPXSPEECHCONFIGHANDLE hspeechconfig, SPXAUDIOCONFIGHANDLE haudioOuput); +SPXAPI synthesizer_create_speech_synthesizer_from_auto_detect_source_lang_config(SPXSYNTHHANDLE* phsynth, SPXSPEECHCONFIGHANDLE hspeechconfig, SPXAUTODETECTSOURCELANGCONFIGHANDLE hautoDetectSourceLangConfig, SPXAUDIOCONFIGHANDLE haudioOutput); +SPXAPI dialog_service_connector_create_dialog_service_connector_from_config(SPXRECOHANDLE* phreco, SPXSPEECHCONFIGHANDLE hspeechconfig, SPXAUDIOCONFIGHANDLE haudioInput); +//SPXAPI recognizer_create_conversation_transcriber_from_config(SPXRECOHANDLE* phreco, SPXAUDIOCONFIGHANDLE haudioInput); +SPXAPI recognizer_create_conversation_transcriber_from_config(SPXRECOHANDLE* phreco, SPXSPEECHCONFIGHANDLE hspeechconfig, SPXAUDIOCONFIGHANDLE haudioInput); +SPXAPI recognizer_create_conversation_transcriber_from_auto_detect_source_lang_config(SPXRECOHANDLE* phreco, SPXSPEECHCONFIGHANDLE hspeechconfig, SPXAUTODETECTSOURCELANGCONFIGHANDLE hautoDetectSourceLangConfig, SPXAUDIOCONFIGHANDLE haudioInput); +SPXAPI recognizer_create_conversation_transcriber_from_source_lang_config(SPXRECOHANDLE* phreco, SPXSPEECHCONFIGHANDLE hspeechconfig, SPXSOURCELANGCONFIGHANDLE hSourceLangConfig, SPXAUDIOCONFIGHANDLE haudioInput); +SPXAPI recognizer_join_conversation(SPXCONVERSATIONHANDLE hconv, SPXRECOHANDLE hreco); +SPXAPI recognizer_leave_conversation(SPXRECOHANDLE hreco); +SPXAPI recognizer_create_meeting_transcriber_from_config(SPXRECOHANDLE* phreco, SPXAUDIOCONFIGHANDLE haudioInput); +SPXAPI recognizer_join_meeting(SPXMEETINGHANDLE hmeeting, SPXRECOHANDLE hreco); +SPXAPI recognizer_leave_meeting(SPXRECOHANDLE hreco); +SPXAPI transcriber_get_participants_list(SPXRECOHANDLE hreco, SPXPARTICIPANTHANDLE* participants, int size); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_grammar.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_grammar.h new file mode 100644 index 0000000..2833055 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_grammar.h @@ -0,0 +1,33 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_grammar.h: Public API declarations for Grammar related C methods and typedefs +// + +#pragma once +#include + +typedef enum +{ + // A Recognition Factor will apply to grammars that are referenced as individual words. + PartialPhrase = 1 +} GrammarList_RecognitionFactorScope; + +SPXAPI_(bool) grammar_handle_is_valid(SPXGRAMMARHANDLE hgrammar); +SPXAPI phrase_list_grammar_from_recognizer_by_name(SPXGRAMMARHANDLE* hgrammar, SPXRECOHANDLE hreco, const char* name); +SPXAPI grammar_handle_release(SPXGRAMMARHANDLE hgrammar); + +SPXAPI phrase_list_grammar_add_phrase(SPXGRAMMARHANDLE hgrammar, SPXPHRASEHANDLE hphrase); +SPXAPI phrase_list_grammar_clear(SPXGRAMMARHANDLE hgrammar); + +SPXAPI_(bool) grammar_phrase_handle_is_valid(SPXPHRASEHANDLE hphrase); +SPXAPI grammar_phrase_create_from_text(SPXPHRASEHANDLE* hphrase, const char* phrase); +SPXAPI grammar_phrase_handle_release(SPXPHRASEHANDLE hphrase); + +SPXAPI grammar_create_from_storage_id(SPXGRAMMARHANDLE *hgrammarlist, const char *id); +SPXAPI grammar_list_from_recognizer(SPXGRAMMARHANDLE *hgrammarlist, SPXRECOHANDLE hreco); +SPXAPI grammar_list_add_grammar(SPXGRAMMARHANDLE hgrammarlist, SPXGRAMMARHANDLE hgrammar); +SPXAPI grammar_list_set_recognition_factor(SPXGRAMMARHANDLE hgrammarlist, double factor, GrammarList_RecognitionFactorScope scope); +SPXAPI class_language_model_from_storage_id(SPXGRAMMARHANDLE* hclm, const char *storageid); +SPXAPI class_language_model_assign_class(SPXGRAMMARHANDLE hclm, const char *classname, SPXGRAMMARHANDLE hgrammar); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_hybrid_speech_config.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_hybrid_speech_config.h new file mode 100644 index 0000000..945c3ee --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_hybrid_speech_config.h @@ -0,0 +1,9 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include + +SPXAPI hybrid_speech_config_create(SPXSPEECHCONFIGHANDLE* hconfig, SPXSPEECHCONFIGHANDLE hcloudSpeechConfig, SPXSPEECHCONFIGHANDLE hembeddedSpeechConfig); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_intent_recognizer.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_intent_recognizer.h new file mode 100644 index 0000000..241cf99 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_intent_recognizer.h @@ -0,0 +1,16 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_intent_recognizer.h: Public API declarations for IntentRecognizer related C methods and typedefs +// + +#pragma once +#include + +SPXAPI intent_recognizer_add_intent(SPXRECOHANDLE hreco, const char* intentId, SPXTRIGGERHANDLE htrigger); +SPXAPI intent_recognizer_add_intent_with_model_id(SPXRECOHANDLE hreco, SPXTRIGGERHANDLE htrigger, const char* modelId); +SPXAPI intent_recognizer_recognize_text_once(SPXRECOHANDLE hreco, const char* text, SPXRESULTHANDLE* hresult); +SPXAPI intent_recognizer_clear_language_models(SPXRECOHANDLE hreco); +SPXAPI intent_recognizer_import_pattern_matching_model(SPXRECOHANDLE hreco, const char* jsonData); +SPXAPI intent_recognizer_add_conversational_language_understanding_model(SPXRECOHANDLE hreco, const char* languageResourceKey, const char* endpoint, const char* projectName, const char* deploymentName); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_intent_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_intent_result.h new file mode 100644 index 0000000..491121b --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_intent_result.h @@ -0,0 +1,11 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_intent_result.h: Public API declarations for IntentResult related C methods and enumerations +// + +#pragma once +#include + +SPXAPI intent_result_get_intent_id(SPXRESULTHANDLE hresult, char* pszIntentId, uint32_t cchIntentId); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_intent_trigger.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_intent_trigger.h new file mode 100644 index 0000000..50a49e5 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_intent_trigger.h @@ -0,0 +1,17 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_intent_trigger.h: Public API declarations for IntentTrigger related C methods and typedefs +// + +#pragma once +#include + + +SPXAPI_(bool) intent_trigger_handle_is_valid(SPXTRIGGERHANDLE htrigger); + +SPXAPI intent_trigger_create_from_phrase(SPXTRIGGERHANDLE* htrigger, const char* phrase); +SPXAPI intent_trigger_create_from_language_understanding_model(SPXTRIGGERHANDLE* htrigger, SPXLUMODELHANDLE hlumodel, const char* intentName); + +SPXAPI intent_trigger_handle_release(SPXTRIGGERHANDLE htrigger); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_json.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_json.h new file mode 100644 index 0000000..02e27ba --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_json.h @@ -0,0 +1,37 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/azai/vision/license for the full license information. +// + +#pragma once +#include + +SPXAPI__(const char*) ai_core_string_create(const char* str, size_t size); +SPXAPI_(void) ai_core_string_free(const char* str); + +SPXAPI_(int) ai_core_json_parser_create(SPXHANDLE* parser, const char* json, size_t jsize); // returns item for root +SPXAPI_(bool) ai_core_json_parser_handle_is_valid(SPXHANDLE parser); +SPXAPI ai_core_json_parser_handle_release(SPXHANDLE parser); + +SPXAPI_(int) ai_core_json_builder_create(SPXHANDLE* builder, const char* json, size_t jsize); // returns item for root +SPXAPI_(bool) ai_core_json_builder_handle_is_valid(SPXHANDLE builder); +SPXAPI ai_core_json_builder_handle_release(SPXHANDLE builder); + +SPXAPI_(int) ai_core_json_item_count(SPXHANDLE parserOrBuilder, int item); +SPXAPI_(int) ai_core_json_item_at(SPXHANDLE parserOrBuilder, int item, int index, const char* find); // returns item found +SPXAPI_(int) ai_core_json_item_next(SPXHANDLE parserOrBuilder, int item); // returns next item +SPXAPI_(int) ai_core_json_item_name(SPXHANDLE parserOrBuilder, int item); // returns item representing name of item specified + +SPXAPI_(int) ai_core_json_value_kind(SPXHANDLE parserOrBuilder, int item); +SPXAPI_(bool) ai_core_json_value_as_bool(SPXHANDLE parserOrBuilder, int item, bool defaultValue); +SPXAPI_(double) ai_core_json_value_as_double(SPXHANDLE parserOrBuilder, int item, double defaultValue); +SPXAPI_(int64_t) ai_core_json_value_as_int(SPXHANDLE parserOrBuilder, int item, int64_t defaultValue); +SPXAPI_(uint64_t) ai_core_json_value_as_uint(SPXHANDLE parserOrBuilder, int item, uint64_t defaultValue); + +SPXAPI__(const char*) ai_core_json_value_as_string_ptr(SPXHANDLE parserOrBuilder, int item, size_t* size); + +SPXAPI__(const char*) ai_core_json_value_as_string_copy(SPXHANDLE parserOrBuilder, int item, const char* defaultValue); +SPXAPI__(const char*) ai_core_json_value_as_json_copy(SPXHANDLE parserOrBuilder, int item); + +SPXAPI_(int) ai_core_json_builder_item_add(SPXHANDLE builder, int item, int index, const char* find); +SPXAPI ai_core_json_builder_item_set(SPXHANDLE builder, int item, const char* json, size_t jsize, int kind, const char* str, size_t ssize, bool boolean, int integer, double number); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_keyword_recognition_model.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_keyword_recognition_model.h new file mode 100644 index 0000000..45f1ae3 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_keyword_recognition_model.h @@ -0,0 +1,17 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_keyword_recognition_model.h: Public API declarations for KeywordRecognitionModel related C methods and typedefs +// + +#pragma once +#include + + +SPXAPI_(bool) keyword_recognition_model_handle_is_valid(SPXKEYWORDHANDLE hkeyword); +SPXAPI keyword_recognition_model_handle_release(SPXKEYWORDHANDLE hkeyword); + +SPXAPI keyword_recognition_model_create_from_file(const char* fileName, SPXKEYWORDHANDLE* phkwmodel); +SPXAPI keyword_recognition_model_create_from_config(SPXSPEECHCONFIGHANDLE hconfig, SPXKEYWORDHANDLE* phkwmodel); +SPXAPI keyword_recognition_model_add_user_defined_wake_word(SPXKEYWORDHANDLE hkwmodel, const char* wakeWord); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_language_understanding_model.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_language_understanding_model.h new file mode 100644 index 0000000..c86d5b4 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_language_understanding_model.h @@ -0,0 +1,18 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_language_understanding_model.h: Public API declarations for LanguageUnderstandingModel related C methods and typedefs +// + +#pragma once +#include + +SPXAPI_(bool) language_understanding_model_handle_is_valid(SPXLUMODELHANDLE hlumodel); + +SPXAPI language_understanding_model_create_from_uri(SPXLUMODELHANDLE* hlumodel, const char* uri); +SPXAPI language_understanding_model_create_from_app_id(SPXLUMODELHANDLE* hlumodel, const char* appId); +SPXAPI language_understanding_model_create_from_subscription(SPXLUMODELHANDLE* hlumodel, const char* subscriptionKey, const char* appId, const char* region); + +SPXAPI language_understanding_model__handle_release(SPXLUMODELHANDLE hlumodel); +SPXAPI__(const char *) language_understanding_model_get_model_id(SPXLUMODELHANDLE hlumodel); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_meeting.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_meeting.h new file mode 100644 index 0000000..b39a60f --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_meeting.h @@ -0,0 +1,28 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_meeting.h: Public API declarations for meeting related C methods and typedefs +// + +#pragma once +#include + +SPXAPI meeting_create_from_config(SPXMEETINGHANDLE* phmeeting, SPXSPEECHCONFIGHANDLE hspeechconfig, const char* id); +SPXAPI meeting_update_participant_by_user_id(SPXMEETINGHANDLE hconv, bool add, const char* userId); +SPXAPI meeting_update_participant_by_user(SPXMEETINGHANDLE hconv, bool add, SPXUSERHANDLE huser); +SPXAPI meeting_update_participant(SPXMEETINGHANDLE hconv, bool add, SPXPARTICIPANTHANDLE hparticipant); +SPXAPI meeting_get_meeting_id(SPXMEETINGHANDLE hconv, char* id, size_t size); +SPXAPI meeting_end_meeting(SPXMEETINGHANDLE hconv); +SPXAPI meeting_get_property_bag(SPXMEETINGHANDLE hconv, SPXPROPERTYBAGHANDLE* phpropbag); +SPXAPI meeting_release_handle(SPXHANDLE handle); + +SPXAPI meeting_start_meeting(SPXMEETINGHANDLE hconv); +SPXAPI meeting_delete_meeting(SPXMEETINGHANDLE hconv); +SPXAPI meeting_lock_meeting(SPXMEETINGHANDLE hconv); +SPXAPI meeting_unlock_meeting(SPXMEETINGHANDLE hconv); +SPXAPI meeting_mute_all_participants(SPXMEETINGHANDLE hconv); +SPXAPI meeting_unmute_all_participants(SPXMEETINGHANDLE hconv); +SPXAPI meeting_mute_participant(SPXMEETINGHANDLE hconv, const char * participantId); +SPXAPI meeting_unmute_participant(SPXMEETINGHANDLE hconv, const char * participantId); + diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_meeting_transcription_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_meeting_transcription_result.h new file mode 100644 index 0000000..bcfdd35 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_meeting_transcription_result.h @@ -0,0 +1,12 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_meeting_transcriber_result.h: Public API declarations for MeetingTranscriberResult related C methods and enumerations +// + +#pragma once +#include + +SPXAPI meeting_transcription_result_get_user_id(SPXRESULTHANDLE hresult, char* pszUserId, uint32_t cchUserId); +SPXAPI meeting_transcription_result_get_utterance_id(SPXRESULTHANDLE hresult, char* pszUtteranceId, uint32_t cchUtteranceId); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_operations.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_operations.h new file mode 100644 index 0000000..ed556d7 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_operations.h @@ -0,0 +1,12 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_operations.h: Public API declaration for common operation methods in the C API layer. +// + +#pragma once +#include + +SPXAPI speechapi_async_handle_release(SPXASYNCHANDLE h_async); +SPXAPI speechapi_async_wait_for(SPXASYNCHANDLE h_async, uint32_t milliseconds); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_participant.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_participant.h new file mode 100644 index 0000000..1ddb4c8 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_participant.h @@ -0,0 +1,15 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_participant.h: Public API declarations for conversation transcriber participant related C methods and enumerations +// + +#pragma once +#include + +SPXAPI participant_create_handle(SPXPARTICIPANTHANDLE* hparticipant, const char* userId, const char* preferred_language, const char* voice_signature); +SPXAPI participant_release_handle(SPXPARTICIPANTHANDLE hparticipant); +SPXAPI participant_set_preferred_langugage(SPXPARTICIPANTHANDLE hparticipant, const char* preferred_language); +SPXAPI participant_set_voice_signature(SPXPARTICIPANTHANDLE hparticipant, const char* voice_signature); +SPXAPI participant_get_property_bag(SPXPARTICIPANTHANDLE hparticipant, SPXPROPERTYBAGHANDLE* hpropbag); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_pattern_matching_model.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_pattern_matching_model.h new file mode 100644 index 0000000..264f063 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_pattern_matching_model.h @@ -0,0 +1,33 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_pattern_matching_model.h: Public API declarations for PatternMatchingModel related C methods and typedefs +// + +#pragma once +#include + +SPXAPI_(bool) pattern_matching_model_handle_is_valid(SPXLUMODELHANDLE hlumodel); + +SPXAPI pattern_matching_model_create(SPXLUMODELHANDLE* hlumodel, SPXRECOHANDLE hIntentReco, const char* id); +SPXAPI pattern_matching_model_create_from_id(SPXLUMODELHANDLE* hlumodel, const char* id); + +typedef SPXAPI_RESULTTYPE(SPXAPI_CALLTYPE* PATTERN_MATCHING_MODEL_GET_STR_FROM_INDEX)(void* context, size_t index, const char** str, size_t* size); + +SPXAPI pattern_matching_model_add_entity( + SPXLUMODELHANDLE hlumodel, + const char* id, + int32_t type, + int32_t mode, + size_t numPhrases, + void* phraseContext, + PATTERN_MATCHING_MODEL_GET_STR_FROM_INDEX phraseGetter); + +SPXAPI pattern_matching_model_add_intent( + SPXLUMODELHANDLE hlumodel, + const char* id, + uint32_t priority, + size_t numPhrases, + void* phraseContext, + PATTERN_MATCHING_MODEL_GET_STR_FROM_INDEX phraseGetter); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_pronunciation_assessment_config.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_pronunciation_assessment_config.h new file mode 100644 index 0000000..1d5df85 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_pronunciation_assessment_config.h @@ -0,0 +1,33 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include + +typedef enum +{ + PronunciationAssessmentGradingSystem_FivePoint = 1, + PronunciationAssessmentGradingSystem_HundredMark = 2 +} Pronunciation_Assessment_Grading_System; + +typedef enum +{ + PronunciationAssessmentGranularity_Phoneme = 1, + PronunciationAssessmentGranularity_Word = 2, + PronunciationAssessmentGranularity_FullText = 3 +} Pronunciation_Assessment_Granularity; + +SPXAPI create_pronunciation_assessment_config(SPXPRONUNCIATIONASSESSMENTCONFIGHANDLE* hPronunciationAssessmentConfig, + const char* referenceText, + Pronunciation_Assessment_Grading_System gradingSystem, + Pronunciation_Assessment_Granularity granularity, + bool enableMiscue); +SPXAPI create_pronunciation_assessment_config_from_json(SPXPRONUNCIATIONASSESSMENTCONFIGHANDLE* hPronunciationAssessmentConfig, const char* json); +SPXAPI_(bool) pronunciation_assessment_config_is_handle_valid(SPXPRONUNCIATIONASSESSMENTCONFIGHANDLE hPronunciationAssessmentConfig); +SPXAPI pronunciation_assessment_config_release(SPXPRONUNCIATIONASSESSMENTCONFIGHANDLE hPronunciationAssessmentConfig); +SPXAPI pronunciation_assessment_config_get_property_bag( + SPXPRONUNCIATIONASSESSMENTCONFIGHANDLE hPronunciationAssessmentConfig, SPXPROPERTYBAGHANDLE* hpropbag); +SPXAPI__(const char*) pronunciation_assessment_config_to_json(SPXPRONUNCIATIONASSESSMENTCONFIGHANDLE hPronunciationAssessmentConfig); +SPXAPI pronunciation_assessment_config_apply_to_recognizer(SPXPRONUNCIATIONASSESSMENTCONFIGHANDLE hPronunciationAssessmentConfig, SPXRECOHANDLE hreco); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_property_bag.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_property_bag.h new file mode 100644 index 0000000..39413df --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_property_bag.h @@ -0,0 +1,159 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_property_bag.h: Public API declarations for Property Bag related C methods +// + +#pragma once +#include + +SPXAPI property_bag_create(SPXPROPERTYBAGHANDLE* hpropbag); +SPXAPI_(bool) property_bag_is_valid(SPXPROPERTYBAGHANDLE hpropbag); +SPXAPI property_bag_set_string(SPXPROPERTYBAGHANDLE hpropbag, int id, const char* name, const char* value); +SPXAPI__(const char*) property_bag_get_string(SPXPROPERTYBAGHANDLE hpropbag, int id, const char* name, const char* defaultValue); +SPXAPI property_bag_free_string(const char* value); +SPXAPI property_bag_release(SPXPROPERTYBAGHANDLE hpropbag); +SPXAPI property_bag_copy(SPXPROPERTYBAGHANDLE hfrom, SPXPROPERTYBAGHANDLE hto); + +// NOTE: Currently this enum is duplicated with C++ side, +// because SWIG cannot properly resolve conditional compilation. +#ifndef __cplusplus +enum PropertyId +{ + SpeechServiceConnection_Key = 1000, + SpeechServiceConnection_Endpoint = 1001, + SpeechServiceConnection_Region = 1002, + SpeechServiceAuthorization_Token = 1003, + SpeechServiceAuthorization_Type = 1004, + SpeechServiceConnection_EndpointId = 1005, + SpeechServiceConnection_Host = 1006, + + SpeechServiceConnection_ProxyHostName = 1100, + SpeechServiceConnection_ProxyPort = 1101, + SpeechServiceConnection_ProxyUserName = 1102, + SpeechServiceConnection_ProxyPassword = 1103, + SpeechServiceConnection_Url = 1104, + SpeechServiceConnection_ProxyHostBypass = 1105, + + SpeechServiceConnection_TranslationToLanguages = 2000, + SpeechServiceConnection_TranslationVoice = 2001, + SpeechServiceConnection_TranslationFeatures = 2002, + SpeechServiceConnection_IntentRegion = 2003, + + SpeechServiceConnection_RecoMode = 3000, + SpeechServiceConnection_RecoLanguage = 3001, + Speech_SessionId = 3002, + SpeechServiceConnection_UserDefinedQueryParameters = 3003, + SpeechServiceConnection_RecoModelBackend = 3004, + SpeechServiceConnection_RecoModelName = 3005, + SpeechServiceConnection_RecoModelKey = 3006, + SpeechServiceConnection_RecoModelIniFile = 3007, + + SpeechServiceConnection_SynthLanguage = 3100, + SpeechServiceConnection_SynthVoice = 3101, + SpeechServiceConnection_SynthOutputFormat = 3102, + SpeechServiceConnection_SynthEnableCompressedAudioTransmission = 3103, + SpeechServiceConnection_SynthBackend = 3110, + SpeechServiceConnection_SynthOfflineDataPath = 3112, + SpeechServiceConnection_SynthOfflineVoice = 3113, + SpeechServiceConnection_SynthModelKey = 3114, + SpeechServiceConnection_VoicesListEndpoint = 3130, + + SpeechServiceConnection_InitialSilenceTimeoutMs = 3200, + SpeechServiceConnection_EndSilenceTimeoutMs = 3201, + SpeechServiceConnection_EnableAudioLogging = 3202, + SpeechServiceConnection_LanguageIdMode = 3205, + SpeechServiceConnection_TranslationCategoryId = 3206, + + SpeechServiceConnection_AutoDetectSourceLanguages = 3300, + SpeechServiceConnection_AutoDetectSourceLanguageResult = 3301, + + SpeechServiceResponse_RequestDetailedResultTrueFalse = 4000, + SpeechServiceResponse_RequestProfanityFilterTrueFalse = 4001, + SpeechServiceResponse_ProfanityOption = 4002, + SpeechServiceResponse_PostProcessingOption = 4003, + SpeechServiceResponse_RequestWordLevelTimestamps = 4004, + SpeechServiceResponse_StablePartialResultThreshold = 4005, + SpeechServiceResponse_OutputFormatOption = 4006, + SpeechServiceResponse_RequestSnr = 4007, + + SpeechServiceResponse_TranslationRequestStablePartialResult = 4100, + + SpeechServiceResponse_RequestWordBoundary = 4200, + SpeechServiceResponse_RequestPunctuationBoundary = 4201, + SpeechServiceResponse_RequestSentenceBoundary = 4202, + SpeechServiceResponse_SynthesisEventsSyncToAudio = 4210, + + SpeechServiceResponse_JsonResult = 5000, + SpeechServiceResponse_JsonErrorDetails = 5001, + SpeechServiceResponse_RecognitionLatencyMs = 5002, + SpeechServiceResponse_RecognitionBackend = 5003, + + SpeechServiceResponse_SynthesisFirstByteLatencyMs = 5010, + SpeechServiceResponse_SynthesisFinishLatencyMs = 5011, + SpeechServiceResponse_SynthesisUnderrunTimeMs = 5012, + SpeechServiceResponse_SynthesisConnectionLatencyMs = 5013, + SpeechServiceResponse_SynthesisNetworkLatencyMs = 5014, + SpeechServiceResponse_SynthesisServiceLatencyMs = 5015, + SpeechServiceResponse_DiarizeIntermediateResults = 5025, + + CancellationDetails_Reason = 6000, + CancellationDetails_ReasonText = 6001, + CancellationDetails_ReasonDetailedText = 6002, + + LanguageUnderstandingServiceResponse_JsonResult = 7000, + + AudioConfig_DeviceNameForCapture = 8000, + AudioConfig_NumberOfChannelsForCapture = 8001, + AudioConfig_SampleRateForCapture = 8002, + AudioConfig_BitsPerSampleForCapture = 8003, + AudioConfig_AudioSource = 8004, + AudioConfig_DeviceNameForRender = 8005, + AudioConfig_PlaybackBufferLengthInMs = 8006, + + Speech_LogFilename = 9001, + Speech_SegmentationSilenceTimeoutMs = 9002, + Speech_SegmentationMaximumTimeMs = 9003, + Speech_SegmentationMaximumTimeMs = 9004, + + Conversation_ApplicationId = 10000, + Conversation_DialogType = 10001, + Conversation_Initial_Silence_Timeout = 10002, + Conversation_From_Id = 10003, + Conversation_Conversation_Id = 10004, + Conversation_Custom_Voice_Deployment_Ids = 10005, + Conversation_Speech_Activity_Template = 10006, + Conversation_ParticipantId = 10007, + DataBuffer_TimeStamp = 11001, + DataBuffer_UserId = 11002, + + PronunciationAssessment_ReferenceText = 12001, + PronunciationAssessment_GradingSystem = 12002, + PronunciationAssessment_Granularity = 12003, + PronunciationAssessment_EnableMiscue = 12005, + PronunciationAssessment_PhonemeAlphabet = 12006, + PronunciationAssessment_NBestPhonemeCount = 12007, + PronunciationAssessment_EnableProsodyAssessment = 12008, + PronunciationAssessment_Json = 12009, + PronunciationAssessment_Params = 12010, + PronunciationAssessment_ContentTopic = 12020, + SpeakerRecognition_Api_Version = 13001, + + SpeechTranslation_ModelName = 13100, + SpeechTranslation_ModelKey = 13101, + + KeywordRecognition_ModelName = 13200, + KeywordRecognition_ModelKey = 13201, + + EmbeddedSpeech_EnablePerformanceMetrics = 13300 +}; + +typedef enum _ParticipantChangedReason +{ + JoinedConversation, + LeftConversation, + Updated +} ParticipantChangedReason; +#endif + diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_recognizer.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_recognizer.h new file mode 100644 index 0000000..69caba0 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_recognizer.h @@ -0,0 +1,66 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_recognizer.h: Public API declarations for Recognizer related C methods and typedefs +// + +#pragma once +#include + + +SPXAPI_(bool) recognizer_handle_is_valid(SPXRECOHANDLE hreco); +SPXAPI recognizer_handle_release(SPXRECOHANDLE hreco); + +SPXAPI_(bool) recognizer_async_handle_is_valid(SPXASYNCHANDLE hasync); +SPXAPI recognizer_async_handle_release(SPXASYNCHANDLE hasync); + +SPXAPI_(bool) recognizer_result_handle_is_valid(SPXRESULTHANDLE hresult); +SPXAPI recognizer_result_handle_release(SPXRESULTHANDLE hresult); + +SPXAPI_(bool) recognizer_event_handle_is_valid(SPXEVENTHANDLE hevent); +SPXAPI recognizer_event_handle_release(SPXEVENTHANDLE hevent); + +SPXAPI recognizer_get_property_bag(SPXRECOHANDLE hreco, SPXPROPERTYBAGHANDLE* hpropbag); + +SPXAPI recognizer_recognize_once(SPXRECOHANDLE hreco, SPXRESULTHANDLE* phresult); +SPXAPI recognizer_recognize_once_async(SPXRECOHANDLE hreco, SPXASYNCHANDLE* phasync); +SPXAPI recognizer_recognize_text_once_async(SPXRECOHANDLE hreco, const char* text, SPXASYNCHANDLE* phasync); +SPXAPI recognizer_recognize_once_async_wait_for(SPXASYNCHANDLE hasync, uint32_t milliseconds, SPXRESULTHANDLE* phresult); + +SPXAPI recognizer_start_continuous_recognition(SPXRECOHANDLE hreco); +SPXAPI recognizer_start_continuous_recognition_async(SPXRECOHANDLE hreco, SPXASYNCHANDLE* phasync); +SPXAPI recognizer_start_continuous_recognition_async_wait_for(SPXASYNCHANDLE hasync, uint32_t milliseconds); + +SPXAPI recognizer_stop_continuous_recognition(SPXRECOHANDLE hreco); +SPXAPI recognizer_stop_continuous_recognition_async(SPXRECOHANDLE hreco, SPXASYNCHANDLE* phasync); +SPXAPI recognizer_stop_continuous_recognition_async_wait_for(SPXASYNCHANDLE hasync, uint32_t milliseconds); + +SPXAPI recognizer_start_keyword_recognition(SPXRECOHANDLE hreco, SPXKEYWORDHANDLE hkeyword); +SPXAPI recognizer_start_keyword_recognition_async(SPXRECOHANDLE hreco, SPXKEYWORDHANDLE hkeyword, SPXASYNCHANDLE* phasync); +SPXAPI recognizer_start_keyword_recognition_async_wait_for(SPXASYNCHANDLE hasync, uint32_t milliseconds); + +SPXAPI recognizer_recognize_keyword_once(SPXRECOHANDLE hreco, SPXKEYWORDHANDLE hkeyword, SPXRESULTHANDLE* phresult); +SPXAPI recognizer_recognize_keyword_once_async(SPXRECOHANDLE hreco, SPXKEYWORDHANDLE hkeyword, SPXASYNCHANDLE* phasync); +SPXAPI recognizer_recognize_keyword_once_async_wait_for(SPXASYNCHANDLE hasync, uint32_t milliseconds, SPXRESULTHANDLE* phresult); + +SPXAPI recognizer_stop_keyword_recognition(SPXRECOHANDLE hreco); +SPXAPI recognizer_stop_keyword_recognition_async(SPXRECOHANDLE hreco, SPXASYNCHANDLE* phasync); +SPXAPI recognizer_stop_keyword_recognition_async_wait_for(SPXASYNCHANDLE hasync, uint32_t milliseconds); + +typedef void (*PSESSION_CALLBACK_FUNC)(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext); +SPXAPI recognizer_session_started_set_callback(SPXRECOHANDLE hreco, PSESSION_CALLBACK_FUNC pCallback, void* pvContext); +SPXAPI recognizer_session_stopped_set_callback(SPXRECOHANDLE hreco, PSESSION_CALLBACK_FUNC pCallback, void* pvContext); + +typedef void (*PRECOGNITION_CALLBACK_FUNC)(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext); +SPXAPI recognizer_recognizing_set_callback(SPXRECOHANDLE hreco, PRECOGNITION_CALLBACK_FUNC pCallback, void* pvContext); +SPXAPI recognizer_recognized_set_callback(SPXRECOHANDLE hreco, PRECOGNITION_CALLBACK_FUNC pCallback, void* pvContext); +SPXAPI recognizer_canceled_set_callback(SPXRECOHANDLE hreco, PRECOGNITION_CALLBACK_FUNC pCallback, void* pvContext); +SPXAPI recognizer_speech_start_detected_set_callback(SPXRECOHANDLE hreco, PRECOGNITION_CALLBACK_FUNC pCallback, void* pvContext); +SPXAPI recognizer_speech_end_detected_set_callback(SPXRECOHANDLE hreco, PRECOGNITION_CALLBACK_FUNC pCallback, void* pvContext); + +SPXAPI recognizer_session_event_get_session_id(SPXEVENTHANDLE hevent, char* pszSessionId, uint32_t cchSessionId); +SPXAPI recognizer_recognition_event_get_offset(SPXEVENTHANDLE hevent, uint64_t *pszOffset); +SPXAPI recognizer_recognition_event_get_result(SPXEVENTHANDLE hevent, SPXRESULTHANDLE* phresult); + +SPXAPI recognizer_connection_event_get_property_bag(SPXEVENTHANDLE hevent, SPXPROPERTYBAGHANDLE* hpropbag); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_result.h new file mode 100644 index 0000000..26572eb --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_result.h @@ -0,0 +1,108 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_result.h: Public API declarations for Result related C methods and enumerations +// + +#pragma once +#include + +enum Result_Reason +{ + ResultReason_NoMatch = 0, + ResultReason_Canceled = 1, + ResultReason_RecognizingSpeech = 2, + ResultReason_RecognizedSpeech = 3, + ResultReason_RecognizingIntent = 4, + ResultReason_RecognizedIntent = 5, + ResultReason_TranslatingSpeech = 6, + ResultReason_TranslatedSpeech = 7, + ResultReason_SynthesizingAudio = 8, + ResultReason_SynthesizingAudioComplete = 9, + ResultReason_RecognizingKeyword = 10, + ResultReason_RecognizedKeyword = 11, + ResultReason_SynthesizingAudioStart = 12 +}; +typedef enum Result_Reason Result_Reason; + +enum Result_CancellationReason +{ + CancellationReason_Error = 1, + CancellationReason_EndOfStream = 2, + CancellationReason_UserCancelled = 3, +}; + +typedef enum Result_CancellationReason Result_CancellationReason; + +enum Result_CancellationErrorCode +{ + CancellationErrorCode_NoError = 0, + CancellationErrorCode_AuthenticationFailure = 1, + CancellationErrorCode_BadRequest = 2, + CancellationErrorCode_TooManyRequests = 3, + CancellationErrorCode_Forbidden = 4, + CancellationErrorCode_ConnectionFailure = 5, + CancellationErrorCode_ServiceTimeout = 6, + CancellationErrorCode_ServiceError = 7, + CancellationErrorCode_ServiceUnavailable = 8, + CancellationErrorCode_RuntimeError = 9 +}; +typedef enum Result_CancellationErrorCode Result_CancellationErrorCode; + +enum Result_NoMatchReason +{ + NoMatchReason_NotRecognized = 1, + NoMatchReason_InitialSilenceTimeout = 2, + NoMatchReason_InitialBabbleTimeout = 3, + NoMatchReason_KeywordNotRecognized = 4, + NoMatchReason_EndSilenceTimeout = 5 +}; +typedef enum Result_NoMatchReason Result_NoMatchReason; + +enum Synthesis_VoiceType +{ + SynthesisVoiceType_OnlineNeural = 1, + SynthesisVoiceType_OnlineStandard = 2, + SynthesisVoiceType_OfflineNeural = 3, + SynthesisVoiceType_OfflineStandard = 4 +}; +typedef enum Synthesis_VoiceType Synthesis_VoiceType; + +SPXAPI result_get_reason(SPXRESULTHANDLE hresult, Result_Reason* reason); +SPXAPI result_get_reason_canceled(SPXRESULTHANDLE hresult, Result_CancellationReason* reason); +SPXAPI result_get_canceled_error_code(SPXRESULTHANDLE hresult, Result_CancellationErrorCode* errorCode); +SPXAPI result_get_no_match_reason(SPXRESULTHANDLE hresult, Result_NoMatchReason* reason); + +SPXAPI result_get_result_id(SPXRESULTHANDLE hresult, char* pszResultId, uint32_t cchResultId); + +SPXAPI result_get_text(SPXRESULTHANDLE hresult, char* pszText, uint32_t cchText); +SPXAPI result_get_offset(SPXRESULTHANDLE hresult, uint64_t* offset); +SPXAPI result_get_duration(SPXRESULTHANDLE hresult, uint64_t* duration); + +SPXAPI result_get_property_bag(SPXRESULTHANDLE hresult, SPXPROPERTYBAGHANDLE* hpropbag); + +SPXAPI synth_result_get_result_id(SPXRESULTHANDLE hresult, char* resultId, uint32_t resultIdLength); +SPXAPI synth_result_get_reason(SPXRESULTHANDLE hresult, Result_Reason* reason); +SPXAPI synth_result_get_reason_canceled(SPXRESULTHANDLE hresult, Result_CancellationReason* reason); +SPXAPI synth_result_get_canceled_error_code(SPXRESULTHANDLE hresult, Result_CancellationErrorCode* errorCode); +SPXAPI synth_result_get_audio_data(SPXRESULTHANDLE hresult, uint8_t* buffer, uint32_t bufferSize, uint32_t* filledSize); +SPXAPI synth_result_get_audio_length_duration(SPXRESULTHANDLE hresult, uint32_t* audioLength, uint64_t* audioDuration); +SPXAPI synth_result_get_audio_format(SPXRESULTHANDLE hresult, SPXAUDIOSTREAMFORMATHANDLE* hformat); +SPXAPI synth_result_get_property_bag(SPXRESULTHANDLE hresult, SPXPROPERTYBAGHANDLE* hpropbag); + +SPXAPI synthesis_voices_result_get_result_id(SPXRESULTHANDLE hresult, char* resultId, uint32_t resultIdLength); +SPXAPI synthesis_voices_result_get_reason(SPXRESULTHANDLE hresult, Result_Reason* reason); +SPXAPI synthesis_voices_result_get_voice_num(SPXRESULTHANDLE hresult, uint32_t* voiceNum); +SPXAPI synthesis_voices_result_get_voice_info(SPXRESULTHANDLE hresult, uint32_t index, SPXRESULTHANDLE* hVoiceInfo); +SPXAPI synthesis_voices_result_get_property_bag(SPXRESULTHANDLE hresult, SPXPROPERTYBAGHANDLE* hpropbag); + +SPXAPI voice_info_handle_release(SPXRESULTHANDLE hVoiceInfo); +SPXAPI__(const char*) voice_info_get_name(SPXRESULTHANDLE hVoiceInfo); +SPXAPI__(const char*) voice_info_get_locale(SPXRESULTHANDLE hVoiceInfo); +SPXAPI__(const char*) voice_info_get_short_name(SPXRESULTHANDLE hVoiceInfo); +SPXAPI__(const char*) voice_info_get_local_name(SPXRESULTHANDLE hVoiceInfo); +SPXAPI__(const char*) voice_info_get_style_list(SPXRESULTHANDLE hVoiceInfo); +SPXAPI__(const char*) voice_info_get_voice_path(SPXRESULTHANDLE hVoiceInfo); +SPXAPI voice_info_get_voice_type(SPXRESULTHANDLE hVoiceInfo, Synthesis_VoiceType* voiceType); +SPXAPI voice_info_get_property_bag(SPXRESULTHANDLE hVoiceInfo, SPXPROPERTYBAGHANDLE* hpropbag); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_session.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_session.h new file mode 100644 index 0000000..c0a8186 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_session.h @@ -0,0 +1,16 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_session.h: Public API declarations for Session related C methods +// + +#pragma once +#include + +SPXAPI session_from_recognizer(SPXRECOHANDLE hreco, SPXSESSIONHANDLE* phsession); + +SPXAPI_(bool) session_handle_is_valid(SPXSESSIONHANDLE hsession); +SPXAPI session_handle_release(SPXSESSIONHANDLE hsession); + +SPXAPI session_get_property_bag(SPXSESSIONHANDLE hsession, SPXPROPERTYBAGHANDLE* hpropbag); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_source_lang_config.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_source_lang_config.h new file mode 100644 index 0000000..53da418 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_source_lang_config.h @@ -0,0 +1,13 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include + +SPXAPI source_lang_config_from_language(SPXSOURCELANGCONFIGHANDLE* hconfig, const char* language); +SPXAPI source_lang_config_from_language_and_endpointId(SPXSOURCELANGCONFIGHANDLE* hconfig, const char* language, const char* endpointId); +SPXAPI_(bool) source_lang_config_is_handle_valid(SPXSOURCELANGCONFIGHANDLE hconfig); +SPXAPI source_lang_config_release(SPXSOURCELANGCONFIGHANDLE hconfig); +SPXAPI source_lang_config_get_property_bag(SPXSOURCELANGCONFIGHANDLE hconfig, SPXPROPERTYBAGHANDLE* hpropbag); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speaker_recognition.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speaker_recognition.h new file mode 100644 index 0000000..84d4b74 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speaker_recognition.h @@ -0,0 +1,37 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_speaker_recogntion.h: c API declarations for speaker recognition. +// + +#pragma once + +#include + +SPXAPI create_voice_profile_client_from_config(SPXVOICEPROFILECLIENTHANDLE* phclient, SPXSPEECHCONFIGHANDLE hSpeechConfig); +SPXAPI voice_profile_client_release_handle(SPXVOICEPROFILECLIENTHANDLE hVoiceClient); +SPXAPI create_voice_profile(SPXVOICEPROFILECLIENTHANDLE hVoiceProfileClient, int id, const char* locale, SPXVOICEPROFILEHANDLE* pProfileHandle); + +SPXAPI enroll_voice_profile(SPXVOICEPROFILECLIENTHANDLE hVoiceProfileClient, SPXVOICEPROFILEHANDLE hProfileHandle, SPXAUDIOCONFIGHANDLE hAudioInput, SPXRESULTHANDLE* phresult); +SPXAPI voice_profile_client_get_property_bag(SPXVOICEPROFILECLIENTHANDLE hVoiceProfileClient, SPXPROPERTYBAGHANDLE* hpropbag); +SPXAPI create_voice_profile_from_id_and_type(SPXVOICEPROFILEHANDLE* phVoiceProfile, const char* id, int type); +SPXAPI voice_profile_get_id(SPXVOICEPROFILEHANDLE hVoiceProfile, char* psz, uint32_t* pcch); +SPXAPI voice_profile_get_type(SPXVOICEPROFILEHANDLE hVoiceProfile, int* ptype); +SPXAPI voice_profile_release_handle(SPXVOICEPROFILEHANDLE hVoiceProfile); +SPXAPI voice_profile_get_property_bag(SPXVOICEPROFILEHANDLE voiceprofilehandle, SPXPROPERTYBAGHANDLE* pProperties); +SPXAPI delete_voice_profile(SPXVOICEPROFILECLIENTHANDLE hclient, SPXVOICEPROFILEHANDLE hProfileHandle, SPXRESULTHANDLE* phresult); +SPXAPI reset_voice_profile(SPXVOICEPROFILECLIENTHANDLE hVoiceProfileClient, SPXVOICEPROFILEHANDLE hProfileHandle, SPXRESULTHANDLE* phresult); +SPXAPI get_profiles_json(SPXVOICEPROFILECLIENTHANDLE hVoiceProfileClient, int type, char** ppsz, size_t* pcch); +SPXAPI retrieve_enrollment_result(SPXVOICEPROFILECLIENTHANDLE hVoiceProfileClient, const char* pId, int type, SPXVOICEPROFILEHANDLE* phVoiceProfile); +SPXAPI get_activation_phrases(SPXVOICEPROFILECLIENTHANDLE hVoiceProfileClient, const char* pLocale, int type, SPXRESULTHANDLE* phresult); +SPXAPI recognizer_create_speaker_recognizer_from_config(SPXSPEAKERIDHANDLE* phspeakerid, SPXSPEECHCONFIGHANDLE hspeechconfig, SPXAUDIOCONFIGHANDLE haudioInput); +SPXAPI speaker_recognizer_release_handle(SPXSPEAKERIDHANDLE phspeakerid); +SPXAPI speaker_recognizer_get_property_bag(SPXSPEAKERIDHANDLE phspeakerid, SPXPROPERTYBAGHANDLE* hpropbag); +SPXAPI speaker_identification_model_create(SPXSIMODELHANDLE* psimodel); +SPXAPI speaker_identification_model_add_profile(SPXSIMODELHANDLE hsimodel, SPXVOICEPROFILEHANDLE hprofile); +SPXAPI speaker_identification_model_release_handle(SPXSIMODELHANDLE hmodel); +SPXAPI speaker_recognizer_identify(SPXSPEAKERIDHANDLE phspeakerid, SPXSIMODELHANDLE hsimodel, SPXRESULTHANDLE* phresult); +SPXAPI speaker_recognizer_verify(SPXSPEAKERIDHANDLE phspeakerid, SPXSVMODELHANDLE hsvmodel, SPXRESULTHANDLE* phresult); +SPXAPI speaker_verification_model_create(SPXSVMODELHANDLE* psvmodel, SPXVOICEPROFILEHANDLE hprofile); +SPXAPI speaker_verification_model_release_handle(SPXSVMODELHANDLE hsvmodel); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speech_config.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speech_config.h new file mode 100644 index 0000000..ef2c521 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speech_config.h @@ -0,0 +1,171 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include + +typedef enum { SpeechOutputFormat_Simple = 0, SpeechOutputFormat_Detailed = 1 } SpeechOutputFormat; + +typedef enum +{ + // raw-8khz-8bit-mono-mulaw + SpeechSynthesisOutputFormat_Raw8Khz8BitMonoMULaw = 1, + + // riff-16khz-16kbps-mono-siren + // Unsupported by the service. Do not use this value. + SpeechSynthesisOutputFormat_Riff16Khz16KbpsMonoSiren = 2, + + // audio-16khz-16kbps-mono-siren + // Unsupported by the service. Do not use this value. + SpeechSynthesisOutputFormat_Audio16Khz16KbpsMonoSiren = 3, + + // audio-16khz-32kbitrate-mono-mp3 + SpeechSynthesisOutputFormat_Audio16Khz32KBitRateMonoMp3 = 4, + + // audio-16khz-128kbitrate-mono-mp3 + SpeechSynthesisOutputFormat_Audio16Khz128KBitRateMonoMp3 = 5, + + // audio-16khz-64kbitrate-mono-mp3 + SpeechSynthesisOutputFormat_Audio16Khz64KBitRateMonoMp3 = 6, + + // audio-24khz-48kbitrate-mono-mp3 + SpeechSynthesisOutputFormat_Audio24Khz48KBitRateMonoMp3 = 7, + + // audio-24khz-96kbitrate-mono-mp3 + SpeechSynthesisOutputFormat_Audio24Khz96KBitRateMonoMp3 = 8, + + // audio-24khz-160kbitrate-mono-mp3 + SpeechSynthesisOutputFormat_Audio24Khz160KBitRateMonoMp3 = 9, + + // raw-16khz-16bit-mono-truesilk + SpeechSynthesisOutputFormat_Raw16Khz16BitMonoTrueSilk = 10, + + // riff-16khz-16bit-mono-pcm + SpeechSynthesisOutputFormat_Riff16Khz16BitMonoPcm = 11, + + // riff-8khz-16bit-mono-pcm + SpeechSynthesisOutputFormat_Riff8Khz16BitMonoPcm = 12, + + // riff-24khz-16bit-mono-pcm + SpeechSynthesisOutputFormat_Riff24Khz16BitMonoPcm = 13, + + // riff-8khz-8bit-mono-mulaw + SpeechSynthesisOutputFormat_Riff8Khz8BitMonoMULaw = 14, + + // raw-16khz-16bit-mono-pcm + SpeechSynthesisOutputFormat_Raw16Khz16BitMonoPcm = 15, + + // raw-24khz-16bit-mono-pcm + SpeechSynthesisOutputFormat_Raw24Khz16BitMonoPcm = 16, + + // raw-8khz-16bit-mono-pcm + SpeechSynthesisOutputFormat_Raw8Khz16BitMonoPcm = 17, + + // ogg-16khz-16bit-mono-opus + SpeechSynthesisOutputFormat_Ogg16khz16BitMonoOpus = 18, + + // ogg-24khz-24bit-mono-opus + SpeechSynthesisOutputFormat_Ogg24Khz16BitMonoOpus = 19, + + // raw-48khz-16bit-mono-pcm + SpeechSynthesisOutputFormat_Raw48Khz16BitMonoPcm = 20, + + // riff-48khz-16bit-mono-pcm + SpeechSynthesisOutputFormat_Riff48Khz16BitMonoPcm = 21, + + // audio-48khz-96kbitrate-mono-mp3 + SpeechSynthesisOutputFormat_Audio48Khz96KBitRateMonoMp3 = 22, + + // audio-48khz-192kbitrate-mono-mp3 + SpeechSynthesisOutputFormat_Audio48Khz192KBitRateMonoMp3 = 23, + + // ogg-48khz-16bit-mono-opus + SpeechSynthesisOutputFormat_Ogg48Khz16BitMonoOpus = 24, + + // webm-16khz-16bit-mono-opus + SpeechSynthesisOutputFormat_Webm16Khz16BitMonoOpus = 25, + + // webm-24khz-16bit-mono-opus + SpeechSynthesisOutputFormat_Webm24Khz16BitMonoOpus = 26, + + // raw-24khz-16bit-mono-truesilk + SpeechSynthesisOutputFormat_Raw24Khz16BitMonoTrueSilk = 27, + + // raw-8khz-8bit-mono-alaw + SpeechSynthesisOutputFormat_Raw8Khz8BitMonoALaw = 28, + + // riff-8khz-8bit-mono-alaw + SpeechSynthesisOutputFormat_Riff8Khz8BitMonoALaw = 29, + + // webm-24khz-16bit-24kbps-mono-opus + // Audio compressed by OPUS codec in a WebM container, with bitrate of 24kbps, optimized for IoT scenario. + SpeechSynthesisOutputFormat_Webm24Khz16Bit24KbpsMonoOpus = 30, + + // audio-16khz-16bit-32kbps-mono-opus + // Audio compressed by OPUS codec without container, with bitrate of 32kbps. + SpeechSynthesisOutputFormat_Audio16Khz16Bit32KbpsMonoOpus = 31, + + // audio-24khz-48bit-mono-opus + // Audio compressed by OPUS codec without container, with bitrate of 48kbps. + SpeechSynthesisOutputFormat_Audio24Khz16Bit48KbpsMonoOpus = 32, + + // audio-24khz-24bit-mono-opus + // Audio compressed by OPUS codec without container, with bitrate of 24kbps. + SpeechSynthesisOutputFormat_Audio24Khz16Bit24KbpsMonoOpus = 33, + + // raw-22050hz-16bit-mono-pcm + // Raw PCM audio at 22050Hz sampling rate and 16-bit depth. + SpeechSynthesisOutputFormat_Raw22050Hz16BitMonoPcm = 34, + + // riff-22050hz-16bit-mono-pcm + // PCM audio at 22050Hz sampling rate and 16-bit depth, with RIFF header. + SpeechSynthesisOutputFormat_Riff22050Hz16BitMonoPcm = 35, + + // raw-44100hz-16bit-mono-pcm + // Raw PCM audio at 44100Hz sampling rate and 16-bit depth. + SpeechSynthesisOutputFormat_Raw44100Hz16BitMonoPcm = 36, + + // riff-44100hz-16bit-mono-pcm + // PCM audio at 44100Hz sampling rate and 16-bit depth, with RIFF header. + SpeechSynthesisOutputFormat_Riff44100Hz16BitMonoPcm = 37, + + /// amr-wb-16000hz + /// AMR-WB audio at 16kHz sampling rate. + /// (Added in 1.24.0) + SpeechSynthesisOutputFormat_AmrWb16000Hz = 38, + + /// g722-16khz-64kbps + /// G.722 audio at 16kHz sampling rate and 64kbps bitrate. + /// (Added in 1.38.0) + SpeechSynthesisOutputFormat_G72216Khz64Kbps = 39, +} Speech_Synthesis_Output_Format; + +typedef enum +{ + // Using URI query parameter to pass property settings to service. + SpeechConfig_ServicePropertyChannel_UriQueryParameter = 0, + + // Using HttpHeader to set a key/value in a HTTP header. + SpeechConfig_ServicePropertyChannel_HttpHeader = 1 +} SpeechConfig_ServicePropertyChannel; + +typedef enum +{ + SpeechConfig_ProfanityMasked = 0, + SpeechConfig_ProfanityRemoved = 1, + SpeechConfig_ProfanityRaw = 2 +} SpeechConfig_ProfanityOption; + +SPXAPI_(bool) speech_config_is_handle_valid(SPXSPEECHCONFIGHANDLE hconfig); +SPXAPI speech_config_from_subscription(SPXSPEECHCONFIGHANDLE* hconfig, const char* subscription, const char* region); +SPXAPI speech_config_from_authorization_token(SPXSPEECHCONFIGHANDLE* hconfig, const char* authToken, const char* region); +SPXAPI speech_config_from_endpoint(SPXSPEECHCONFIGHANDLE * hconfig, const char* endpoint, const char* subscription); +SPXAPI speech_config_from_host(SPXSPEECHCONFIGHANDLE* hconfig, const char* host, const char* subscription); +SPXAPI speech_config_release(SPXSPEECHCONFIGHANDLE hconfig); +SPXAPI speech_config_get_property_bag(SPXSPEECHCONFIGHANDLE hconfig, SPXPROPERTYBAGHANDLE* hpropbag); +SPXAPI speech_config_set_audio_output_format(SPXSPEECHCONFIGHANDLE hconfig, Speech_Synthesis_Output_Format formatId); +SPXAPI speech_config_set_service_property(SPXSPEECHCONFIGHANDLE configHandle, const char* propertyName, const char* propertyValue, SpeechConfig_ServicePropertyChannel channel); +SPXAPI speech_config_set_profanity(SPXSPEECHCONFIGHANDLE configHandle, SpeechConfig_ProfanityOption profanity); + diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speech_recognition_model.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speech_recognition_model.h new file mode 100644 index 0000000..0c0dada --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speech_recognition_model.h @@ -0,0 +1,13 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include + +SPXAPI speech_recognition_model_handle_release(SPXSPEECHRECOMODELHANDLE hmodel); +SPXAPI__(const char*) speech_recognition_model_get_name(SPXSPEECHRECOMODELHANDLE hmodel); +SPXAPI__(const char*) speech_recognition_model_get_locales(SPXSPEECHRECOMODELHANDLE hmodel); +SPXAPI__(const char*) speech_recognition_model_get_path(SPXSPEECHRECOMODELHANDLE hmodel); +SPXAPI__(const char*) speech_recognition_model_get_version(SPXSPEECHRECOMODELHANDLE hmodel); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speech_translation_config.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speech_translation_config.h new file mode 100644 index 0000000..ed2ea7c --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speech_translation_config.h @@ -0,0 +1,16 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include + +SPXAPI speech_translation_config_from_subscription(SPXSPEECHCONFIGHANDLE* configHandle, const char* subscription, const char* region); +SPXAPI speech_translation_config_from_authorization_token(SPXSPEECHCONFIGHANDLE* configHandle, const char* authToken, const char* region); +SPXAPI speech_translation_config_from_endpoint(SPXSPEECHCONFIGHANDLE* configHandle, const char* endpoint, const char* subscription); +SPXAPI speech_translation_config_from_host(SPXSPEECHCONFIGHANDLE* configHandle, const char* host, const char* subscription); + +SPXAPI speech_translation_config_add_target_language(SPXSPEECHCONFIGHANDLE configHandle, const char* language); +SPXAPI speech_translation_config_remove_target_language(SPXSPEECHCONFIGHANDLE configHandle, const char* language); +SPXAPI speech_translation_config_set_custom_model_category_id(SPXSPEECHCONFIGHANDLE configHandle, const char* categoryId); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speech_translation_model.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speech_translation_model.h new file mode 100644 index 0000000..cca8d4d --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_speech_translation_model.h @@ -0,0 +1,14 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include + +SPXAPI speech_translation_model_handle_release(SPXSPEECHRECOMODELHANDLE hmodel); +SPXAPI__(const char*) speech_translation_model_get_name(SPXSPEECHRECOMODELHANDLE hmodel); +SPXAPI__(const char*) speech_translation_model_get_source_languages(SPXSPEECHRECOMODELHANDLE hmodel); +SPXAPI__(const char*) speech_translation_model_get_target_languages(SPXSPEECHRECOMODELHANDLE hmodel); +SPXAPI__(const char*) speech_translation_model_get_path(SPXSPEECHRECOMODELHANDLE hmodel); +SPXAPI__(const char*) speech_translation_model_get_version(SPXSPEECHRECOMODELHANDLE hmodel); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_synthesis_request.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_synthesis_request.h new file mode 100644 index 0000000..90af519 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_synthesis_request.h @@ -0,0 +1,17 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include + +SPXAPI speech_synthesis_request_create(bool textStreamingEnabled, bool isSSML, const char* inputText, uint32_t textLength, SPXREQUESTHANDLE* hrequest); +SPXAPI speech_synthesis_request_set_voice(SPXREQUESTHANDLE hrequest, const char* voice, const char* personalVoice, const char* modelName); +SPXAPI speech_synthesis_request_send_text_piece(SPXREQUESTHANDLE hrequest, const char* text, uint32_t textLength); +SPXAPI speech_synthesis_request_finish(SPXREQUESTHANDLE hrequest); +SPXAPI speech_synthesis_request_handle_is_valid(SPXREQUESTHANDLE hrequest); +SPXAPI speech_synthesis_request_release(SPXREQUESTHANDLE hrequest); + +SPXAPI speech_synthesis_request_get_property_bag(SPXREQUESTHANDLE hrequest, SPXPROPERTYBAGHANDLE* hpropbag); + diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_synthesizer.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_synthesizer.h new file mode 100644 index 0000000..af1385d --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_synthesizer.h @@ -0,0 +1,74 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_synthesizer.h: Public API declarations for Synthesizer related C methods and typedefs +// + +#pragma once +#include +#include +#include + + +enum SpeechSynthesis_BoundaryType +{ + SpeechSynthesis_BoundaryType_Word = 0, + SpeechSynthesis_BoundaryType_Punctuation = 1, + SpeechSynthesis_BoundaryType_Sentence = 2 +}; +typedef enum SpeechSynthesis_BoundaryType SpeechSynthesis_BoundaryType; + +SPXAPI_(bool) synthesizer_handle_is_valid(SPXSYNTHHANDLE hsynth); +SPXAPI synthesizer_handle_release(SPXSYNTHHANDLE hsynth); + +SPXAPI_(bool) synthesizer_async_handle_is_valid(SPXASYNCHANDLE hasync); +SPXAPI synthesizer_async_handle_release(SPXASYNCHANDLE hasync); + +SPXAPI_(bool) synthesizer_result_handle_is_valid(SPXRESULTHANDLE hresult); +SPXAPI synthesizer_result_handle_release(SPXRESULTHANDLE hresult); + +SPXAPI_(bool) synthesizer_event_handle_is_valid(SPXEVENTHANDLE hevent); +SPXAPI synthesizer_event_handle_release(SPXEVENTHANDLE hevent); + +SPXAPI synthesizer_get_property_bag(SPXSYNTHHANDLE hsynth, SPXPROPERTYBAGHANDLE* hpropbag); + +SPXAPI synthesizer_speak_text(SPXSYNTHHANDLE hsynth, const char* text, uint32_t textLength, SPXRESULTHANDLE* phresult); +SPXAPI synthesizer_speak_ssml(SPXSYNTHHANDLE hsynth, const char* ssml, uint32_t ssmlLength, SPXRESULTHANDLE* phresult); +SPXAPI synthesizer_speak_request(SPXSYNTHHANDLE hsynth, SPXREQUESTHANDLE hrequest, SPXRESULTHANDLE* phresult); +SPXAPI synthesizer_speak_text_async(SPXSYNTHHANDLE hsynth, const char* text, uint32_t textLength, SPXASYNCHANDLE* phasync); +SPXAPI synthesizer_speak_ssml_async(SPXSYNTHHANDLE hsynth, const char* ssml, uint32_t ssmlLength, SPXASYNCHANDLE* phasync); +SPXAPI synthesizer_speak_request_async(SPXSYNTHHANDLE hsynth, SPXREQUESTHANDLE hrequest, SPXASYNCHANDLE* phasync); +SPXAPI synthesizer_start_speaking_text(SPXSYNTHHANDLE hsynth, const char* text, uint32_t textLength, SPXRESULTHANDLE* phresult); +SPXAPI synthesizer_start_speaking_ssml(SPXSYNTHHANDLE hsynth, const char* ssml, uint32_t ssmlLength, SPXRESULTHANDLE* phresult); +SPXAPI synthesizer_start_speaking_request(SPXSYNTHHANDLE hsynth, SPXREQUESTHANDLE hrequest, SPXRESULTHANDLE* phresult); +SPXAPI synthesizer_start_speaking_text_async(SPXSYNTHHANDLE hsynth, const char* text, uint32_t textLength, SPXASYNCHANDLE* phasync); +SPXAPI synthesizer_start_speaking_ssml_async(SPXSYNTHHANDLE hsynth, const char* ssml, uint32_t ssmlLength, SPXASYNCHANDLE* phasync); +SPXAPI synthesizer_speak_async_wait_for(SPXASYNCHANDLE hasync, uint32_t milliseconds, SPXRESULTHANDLE* phresult); +SPXAPI synthesizer_stop_speaking(SPXSYNTHHANDLE hsynth); +SPXAPI synthesizer_stop_speaking_async(SPXSYNTHHANDLE hsynth, SPXASYNCHANDLE* phasync); +SPXAPI synthesizer_stop_speaking_async_wait_for(SPXASYNCHANDLE hasync, uint32_t milliseconds); + +SPXAPI synthesizer_get_voices_list(SPXSYNTHHANDLE hsynth, const char* locale, SPXRESULTHANDLE* phresult); +SPXAPI synthesizer_get_voices_list_async(SPXSYNTHHANDLE hsynth, const char* locale, SPXASYNCHANDLE* phasync); +SPXAPI synthesizer_get_voices_list_async_wait_for(SPXASYNCHANDLE hasync, uint32_t milliseconds, SPXRESULTHANDLE* phresult); + +typedef void(*PSYNTHESIS_CALLBACK_FUNC)(SPXSYNTHHANDLE hsynth, SPXEVENTHANDLE hevent, void* pvContext); +SPXAPI synthesizer_started_set_callback(SPXSYNTHHANDLE hsynth, PSYNTHESIS_CALLBACK_FUNC pCallback, void* pvContext); +SPXAPI synthesizer_synthesizing_set_callback(SPXSYNTHHANDLE hsynth, PSYNTHESIS_CALLBACK_FUNC pCallback, void* pvContext); +SPXAPI synthesizer_completed_set_callback(SPXSYNTHHANDLE hsynth, PSYNTHESIS_CALLBACK_FUNC pCallback, void* pvContext); +SPXAPI synthesizer_canceled_set_callback(SPXSYNTHHANDLE hsynth, PSYNTHESIS_CALLBACK_FUNC pCallback, void* pvContext); +SPXAPI synthesizer_word_boundary_set_callback(SPXSYNTHHANDLE hsynth, PSYNTHESIS_CALLBACK_FUNC pCallback, void* pvContext); +SPXAPI synthesizer_viseme_received_set_callback(SPXSYNTHHANDLE hsynth, PSYNTHESIS_CALLBACK_FUNC pCallback, void* pvContext); +SPXAPI synthesizer_bookmark_reached_set_callback(SPXSYNTHHANDLE hsynth, PSYNTHESIS_CALLBACK_FUNC pCallback, void* pvContext); +SPXAPI synthesizer_connection_connected_set_callback(SPXCONNECTIONHANDLE hConnection, CONNECTION_CALLBACK_FUNC pCallback, void * pvContext); +SPXAPI synthesizer_connection_disconnected_set_callback(SPXCONNECTIONHANDLE hConnection, CONNECTION_CALLBACK_FUNC pCallback, void * pvContext); + +SPXAPI synthesizer_synthesis_event_get_result(SPXEVENTHANDLE hevent, SPXRESULTHANDLE* phresult); +SPXAPI synthesizer_word_boundary_event_get_values(SPXEVENTHANDLE hevent, uint64_t *pAudioOffset, uint64_t *pDuration, + uint32_t *pTextOffset, uint32_t *pWordLength, SpeechSynthesis_BoundaryType *pBoundaryType); +SPXAPI synthesizer_event_get_result_id(SPXEVENTHANDLE hEvent, char* resultId, uint32_t resultIdLength); +SPXAPI__(const char*) synthesizer_event_get_text(SPXEVENTHANDLE hEvent); +SPXAPI synthesizer_viseme_event_get_values(SPXEVENTHANDLE hevent, uint64_t* pAudioOffset, uint32_t* pVisemeId); +SPXAPI__(const char*) synthesizer_viseme_event_get_animation(SPXEVENTHANDLE hEvent); +SPXAPI synthesizer_bookmark_event_get_values(SPXEVENTHANDLE hevent, uint64_t* pAudioOffset); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_translation_recognizer.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_translation_recognizer.h new file mode 100644 index 0000000..8912ffe --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_translation_recognizer.h @@ -0,0 +1,17 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + + +#pragma once +#include + + +// Todo: Translation recognizer management API. + +typedef void(*PTRANSLATIONSYNTHESIS_CALLBACK_FUNC)(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext); +SPXAPI translator_synthesizing_audio_set_callback(SPXRECOHANDLE hreco, PTRANSLATIONSYNTHESIS_CALLBACK_FUNC pCallback, void* pvContext); + +SPXAPI translator_add_target_language(SPXRECOHANDLE hreco, const char* language); +SPXAPI translator_remove_target_language(SPXRECOHANDLE hreco, const char* language); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_translation_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_translation_result.h new file mode 100644 index 0000000..60bcae0 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_translation_result.h @@ -0,0 +1,14 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include + +SPXAPI translation_text_result_get_translation_count(SPXRESULTHANDLE handle, size_t * size); +SPXAPI translation_text_result_get_translation(SPXRESULTHANDLE handle, size_t index, char * language, char * text, size_t * language_size, size_t * text_size); + +// audioBuffer: point to the header for storing synthesis audio data. The parameter lengthPointer points to the variable saving the size of buffer. On return, *lengthPointer is set to the size of the buffer returned. +// If textBuffer is nullptr or the length is smaller than the size required, the function returns SPXERR_BUFFER_TOO_SMALL. +SPXAPI translation_synthesis_result_get_audio_data(SPXRESULTHANDLE handle, uint8_t* audioBuffer, size_t* lengthPointer); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_user.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_user.h new file mode 100644 index 0000000..bd4d705 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/speechapi_c_user.h @@ -0,0 +1,13 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_c_user.h: Public API declarations for user related C methods and enumerations +// + +#pragma once +#include + +SPXAPI user_create_from_id(const char* user_id, SPXUSERHANDLE* huser); +SPXAPI user_release_handle(SPXUSERHANDLE huser); +SPXAPI user_get_id(SPXUSERHANDLE huser, char* user_id, size_t user_id_size); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/spxdebug.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/spxdebug.h new file mode 100644 index 0000000..6e0131b --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/spxdebug.h @@ -0,0 +1,548 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// spxdebug.h: Public API definitions for global C Trace/Debug methods and related #defines +// + +#pragma once + +//------------------------------------------------------- +// Re-enabled ability to compile out all macros... +// However, currently still need to keep all macros until +// final review of all macros is complete. +//------------------------------------------------------- +#define SPX_CONFIG_TRACE_INCLUDE_DBG_WITH_ALL 1 + +#ifdef SPX_CONFIG_TRACE_INCLUDE_DBG_WITH_ALL +#if defined(SPX_CONFIG_TRACE_ALL) && !defined(SPX_CONFIG_DBG_TRACE_ALL) && (!defined(DEBUG) || !defined(_DEBUG)) +#define SPX_CONFIG_DBG_TRACE_ALL 1 +#endif +#endif + +//------------------------------------------------------- +// SPX_ and AZAC_ compatibility section +// (must preceed #include ) +//------------------------------------------------------- + +#if defined(SPX_CONFIG_DBG_TRACE_ALL) && !defined(AZAC_CONFIG_DBG_TRACE_ALL) +#define AZAC_CONFIG_DBG_TRACE_ALL SPX_CONFIG_DBG_TRACE_ALL +#elif !defined(SPX_CONFIG_DBG_TRACE_ALL) && defined(AZAC_CONFIG_DBG_TRACE_ALL) +#define SPX_CONFIG_DBG_TRACE_ALL AZAC_CONFIG_DBG_TRACE_ALL +#endif + +#if defined(SPX_CONFIG_DBG_TRACE_VERBOSE) && !defined(AZAC_CONFIG_DBG_TRACE_VERBOSE) +#define AZAC_CONFIG_DBG_TRACE_VERBOSE SPX_CONFIG_DBG_TRACE_VERBOSE +#elif !defined(SPX_CONFIG_DBG_TRACE_VERBOSE) && defined(AZAC_CONFIG_DBG_TRACE_VERBOSE) +#define SPX_CONFIG_DBG_TRACE_VERBOSE AZAC_CONFIG_DBG_TRACE_VERBOSE +#endif + +#if defined(SPX_CONFIG_DBG_TRACE_INFO) && !defined(AZAC_CONFIG_DBG_TRACE_INFO) +#define AZAC_CONFIG_DBG_TRACE_INFO SPX_CONFIG_DBG_TRACE_INFO +#elif !defined(SPX_CONFIG_DBG_TRACE_INFO) && defined(AZAC_CONFIG_DBG_TRACE_INFO) +#define SPX_CONFIG_DBG_TRACE_INFO AZAC_CONFIG_DBG_TRACE_INFO +#endif + +#if defined(SPX_CONFIG_DBG_TRACE_WARNING) && !defined(AZAC_CONFIG_DBG_TRACE_WARNING) +#define AZAC_CONFIG_DBG_TRACE_WARNING SPX_CONFIG_DBG_TRACE_WARNING +#elif !defined(SPX_CONFIG_DBG_TRACE_WARNING) && defined(AZAC_CONFIG_DBG_TRACE_WARNING) +#define SPX_CONFIG_DBG_TRACE_WARNING AZAC_CONFIG_DBG_TRACE_WARNING +#endif + +#if defined(SPX_CONFIG_DBG_TRACE_ERROR) && !defined(AZAC_CONFIG_DBG_TRACE_ERROR) +#define AZAC_CONFIG_DBG_TRACE_ERROR SPX_CONFIG_DBG_TRACE_ERROR +#elif !defined(SPX_CONFIG_DBG_TRACE_ERROR) && defined(AZAC_CONFIG_DBG_TRACE_ERROR) +#define SPX_CONFIG_DBG_TRACE_ERROR AZAC_CONFIG_DBG_TRACE_ERROR +#endif + +#if defined(SPX_CONFIG_DBG_TRACE_FUNCTION) && !defined(AZAC_CONFIG_DBG_TRACE_FUNCTION) +#define AZAC_CONFIG_DBG_TRACE_FUNCTION SPX_CONFIG_DBG_TRACE_FUNCTION +#elif !defined(SPX_CONFIG_DBG_TRACE_FUNCTION) && defined(AZAC_CONFIG_DBG_TRACE_FUNCTION) +#define SPX_CONFIG_DBG_TRACE_FUNCTION AZAC_CONFIG_DBG_TRACE_FUNCTION +#endif + +#if defined(SPX_CONFIG_DBG_TRACE_SCOPE) && !defined(AZAC_CONFIG_DBG_TRACE_SCOPE) +#define AZAC_CONFIG_DBG_TRACE_SCOPE SPX_CONFIG_DBG_TRACE_SCOPE +#elif !defined(SPX_CONFIG_DBG_TRACE_SCOPE) && defined(AZAC_CONFIG_DBG_TRACE_SCOPE) +#define SPX_CONFIG_DBG_TRACE_SCOPE AZAC_CONFIG_DBG_TRACE_SCOPE +#endif + +#if defined(SPX_CONFIG_DBG_TRACE_ASSERT) && !defined(AZAC_CONFIG_DBG_TRACE_ASSERT) +#define AZAC_CONFIG_DBG_TRACE_ASSERT SPX_CONFIG_DBG_TRACE_ASSERT +#elif !defined(SPX_CONFIG_DBG_TRACE_ASSERT) && defined(AZAC_CONFIG_DBG_TRACE_ASSERT) +#define SPX_CONFIG_DBG_TRACE_ASSERT AZAC_CONFIG_DBG_TRACE_ASSERT +#endif + +#if defined(SPX_CONFIG_DBG_TRACE_VERIFY) && !defined(AZAC_CONFIG_DBG_TRACE_VERIFY) +#define AZAC_CONFIG_DBG_TRACE_VERIFY SPX_CONFIG_DBG_TRACE_VERIFY +#elif !defined(SPX_CONFIG_DBG_TRACE_VERIFY) && defined(AZAC_CONFIG_DBG_TRACE_VERIFY) +#define SPX_CONFIG_DBG_TRACE_VERIFY AZAC_CONFIG_DBG_TRACE_VERIFY +#endif + +#if defined(SPX_CONFIG_TRACE_ALL) && !defined(AZAC_CONFIG_TRACE_ALL) +#define AZAC_CONFIG_TRACE_ALL SPX_CONFIG_TRACE_ALL +#elif !defined(SPX_CONFIG_TRACE_ALL) && defined(AZAC_CONFIG_TRACE_ALL) +#define SPX_CONFIG_TRACE_ALL AZAC_CONFIG_TRACE_ALL +#endif + +#if defined(SPX_CONFIG_TRACE_VERBOSE) && !defined(AZAC_CONFIG_TRACE_VERBOSE) +#define AZAC_CONFIG_TRACE_VERBOSE SPX_CONFIG_TRACE_VERBOSE +#elif !defined(SPX_CONFIG_TRACE_VERBOSE) && defined(AZAC_CONFIG_TRACE_VERBOSE) +#define SPX_CONFIG_TRACE_VERBOSE AZAC_CONFIG_TRACE_VERBOSE +#endif + +#if defined(SPX_CONFIG_TRACE_INFO) && !defined(AZAC_CONFIG_TRACE_INFO) +#define AZAC_CONFIG_TRACE_INFO SPX_CONFIG_TRACE_INFO +#elif !defined(SPX_CONFIG_TRACE_INFO) && defined(AZAC_CONFIG_TRACE_INFO) +#define SPX_CONFIG_TRACE_INFO AZAC_CONFIG_TRACE_INFO +#endif + +#if defined(SPX_CONFIG_TRACE_WARNING) && !defined(AZAC_CONFIG_TRACE_WARNING) +#define AZAC_CONFIG_TRACE_WARNING SPX_CONFIG_TRACE_WARNING +#elif !defined(SPX_CONFIG_TRACE_WARNING) && defined(AZAC_CONFIG_TRACE_WARNING) +#define SPX_CONFIG_TRACE_WARNING AZAC_CONFIG_TRACE_WARNING +#endif + +#if defined(SPX_CONFIG_TRACE_ERROR) && !defined(AZAC_CONFIG_TRACE_ERROR) +#define AZAC_CONFIG_TRACE_ERROR SPX_CONFIG_TRACE_ERROR +#elif !defined(SPX_CONFIG_TRACE_ERROR) && defined(AZAC_CONFIG_TRACE_ERROR) +#define SPX_CONFIG_TRACE_ERROR AZAC_CONFIG_TRACE_ERROR +#endif + +#if defined(SPX_CONFIG_TRACE_FUNCTION) && !defined(AZAC_CONFIG_TRACE_FUNCTION) +#define AZAC_CONFIG_TRACE_FUNCTION SPX_CONFIG_TRACE_FUNCTION +#elif !defined(SPX_CONFIG_TRACE_FUNCTION) && defined(AZAC_CONFIG_TRACE_FUNCTION) +#define SPX_CONFIG_TRACE_FUNCTION AZAC_CONFIG_TRACE_FUNCTION +#endif + +#if defined(SPX_CONFIG_TRACE_SCOPE) && !defined(AZAC_CONFIG_TRACE_SCOPE) +#define AZAC_CONFIG_TRACE_SCOPE SPX_CONFIG_TRACE_SCOPE +#elif !defined(SPX_CONFIG_TRACE_SCOPE) && defined(AZAC_CONFIG_TRACE_SCOPE) +#define SPX_CONFIG_TRACE_SCOPE AZAC_CONFIG_TRACE_SCOPE +#endif + +#if defined(SPX_CONFIG_TRACE_THROW_ON_FAIL) && !defined(AZAC_CONFIG_TRACE_THROW_ON_FAIL) +#define AZAC_CONFIG_TRACE_THROW_ON_FAIL SPX_CONFIG_TRACE_THROW_ON_FAIL +#elif !defined(SPX_CONFIG_TRACE_THROW_ON_FAIL) && defined(AZAC_CONFIG_TRACE_THROW_ON_FAIL) +#define SPX_CONFIG_TRACE_THROW_ON_FAIL AZAC_CONFIG_TRACE_THROW_ON_FAIL +#endif + +#if defined(SPX_CONFIG_TRACE_REPORT_ON_FAIL) && !defined(AZAC_CONFIG_TRACE_REPORT_ON_FAIL) +#define AZAC_CONFIG_TRACE_REPORT_ON_FAIL SPX_CONFIG_TRACE_REPORT_ON_FAIL +#elif !defined(SPX_CONFIG_TRACE_REPORT_ON_FAIL) && defined(AZAC_CONFIG_TRACE_REPORT_ON_FAIL) +#define SPX_CONFIG_TRACE_REPORT_ON_FAIL AZAC_CONFIG_TRACE_REPORT_ON_FAIL +#endif + +#if defined(SPX_CONFIG_TRACE_RETURN_ON_FAIL) && !defined(AZAC_CONFIG_TRACE_RETURN_ON_FAIL) +#define AZAC_CONFIG_TRACE_RETURN_ON_FAIL SPX_CONFIG_TRACE_RETURN_ON_FAIL +#elif !defined(SPX_CONFIG_TRACE_RETURN_ON_FAIL) && defined(AZAC_CONFIG_TRACE_RETURN_ON_FAIL) +#define SPX_CONFIG_TRACE_RETURN_ON_FAIL AZAC_CONFIG_TRACE_RETURN_ON_FAIL +#endif + +#if defined(SPX_CONFIG_TRACE_EXITFN_ON_FAIL) && !defined(AZAC_CONFIG_TRACE_EXITFN_ON_FAIL) +#define AZAC_CONFIG_TRACE_EXITFN_ON_FAIL SPX_CONFIG_TRACE_EXITFN_ON_FAIL +#elif !defined(SPX_CONFIG_TRACE_EXITFN_ON_FAIL) && defined(AZAC_CONFIG_TRACE_EXITFN_ON_FAIL) +#define SPX_CONFIG_TRACE_EXITFN_ON_FAIL AZAC_CONFIG_TRACE_EXITFN_ON_FAIL +#endif + +#if !defined(__AZAC_THROW_HR_IMPL) && defined(__SPX_THROW_HR_IMPL) +#define __AZAC_THROW_HR_IMPL __SPX_THROW_HR_IMPL +#elif !defined(__SPX_THROW_HR_IMPL) && defined(__AZAC_THROW_HR_IMPL) +#define __SPX_THROW_HR_IMPL __AZAC_THROW_HR_IMPL +#elif !defined(__AZAC_THROW_HR_IMPL) && !defined(__SPX_THROW_HR_IMPL) +#define __AZAC_THROW_HR_IMPL __azac_rethrow +#define __SPX_THROW_HR_IMPL __azac_rethrow +#else +#error Both __AZAC_THROW_HR_IMPL and __SPX_THROW_HR_IMPL cannot be defined at the same time +#endif + +//------------------------------------------------------- +// SPX_ and SPX_DBG_ macro configuration +//------------------------------------------------------- + +#ifdef SPX_CONFIG_DBG_TRACE_ALL +#define SPX_CONFIG_DBG_TRACE_VERBOSE 1 +#define SPX_CONFIG_DBG_TRACE_INFO 1 +#define SPX_CONFIG_DBG_TRACE_WARNING 1 +#define SPX_CONFIG_DBG_TRACE_ERROR 1 +#define SPX_CONFIG_DBG_TRACE_FUNCTION 1 +#define SPX_CONFIG_DBG_TRACE_SCOPE 1 +#define SPX_CONFIG_DBG_TRACE_ASSERT 1 +#define SPX_CONFIG_DBG_TRACE_VERIFY 1 +#ifndef SPX_CONFIG_TRACE_ALL +#define SPX_CONFIG_TRACE_ALL 1 +#endif +#endif // SPX_CONFIG_DBG_TRACE_ALL + +#ifdef SPX_CONFIG_TRACE_ALL +#define SPX_CONFIG_TRACE_VERBOSE 1 +#define SPX_CONFIG_TRACE_INFO 1 +#define SPX_CONFIG_TRACE_WARNING 1 +#define SPX_CONFIG_TRACE_ERROR 1 +#define SPX_CONFIG_TRACE_FUNCTION 1 +#define SPX_CONFIG_TRACE_SCOPE 1 +#define SPX_CONFIG_TRACE_THROW_ON_FAIL 1 +#define SPX_CONFIG_TRACE_REPORT_ON_FAIL 1 +#define SPX_CONFIG_TRACE_RETURN_ON_FAIL 1 +#define SPX_CONFIG_TRACE_EXITFN_ON_FAIL 1 +#endif // SPX_CONFIG_TRACE_ALL + +//------------------------------------------------------- +// #include section ... +// (must come after everything above) +//------------------------------------------------------- + +#include +#include +#include + +#ifndef _MSC_VER +// macros in this header generate a bunch of +// "ISO C++11 requires at least one argument for the "..." in a variadic macro" errors. +// system_header pragma is the only mechanism that helps to suppress them. +// https://stackoverflow.com/questions/35587137/how-to-suppress-gcc-variadic-macro-argument-warning-for-zero-arguments-for-a-par +// TODO: try to make macros standard-compliant. +#pragma GCC system_header +#endif + +//----------------------------------------------------------- +// SPX_TRACE macro common implementations +//----------------------------------------------------------- + +#define __SPX_TRACE_LEVEL_INFO __AZAC_TRACE_LEVEL_INFO // Trace_Info +#define __SPX_TRACE_LEVEL_WARNING __AZAC_TRACE_LEVEL_WARNING // Trace_Warning +#define __SPX_TRACE_LEVEL_ERROR __AZAC_TRACE_LEVEL_ERROR // Trace_Error +#define __SPX_TRACE_LEVEL_VERBOSE __AZAC_TRACE_LEVEL_VERBOSE // Trace_Verbose + +#ifndef __SPX_DO_TRACE_IMPL +#define __SPX_DO_TRACE_IMPL __AZAC_DO_TRACE_IMPL +#endif + +#define __SPX_DOTRACE(level, title, fileName, lineNumber, ...) \ + __AZAC_DOTRACE(level, title, fileName, lineNumber, ##__VA_ARGS__) + +#define __SPX_TRACE_INFO(title, fileName, lineNumber, msg, ...) \ + __AZAC_TRACE_INFO(title, fileName, lineNumber, msg, ##__VA_ARGS__) + +#define __SPX_TRACE_INFO_IF(cond, title, fileName, lineNumber, msg, ...) \ + __AZAC_TRACE_INFO_IF(cond, title, fileName, lineNumber, msg, ##__VA_ARGS__) + +#define __SPX_TRACE_WARNING(title, fileName, lineNumber, msg, ...) \ + __AZAC_TRACE_WARNING(title, fileName, lineNumber, msg, ##__VA_ARGS__) + +#define __SPX_TRACE_WARNING_IF(cond, title, fileName, lineNumber, msg, ...) \ + __AZAC_TRACE_WARNING_IF(cond, title, fileName, lineNumber, msg, ##__VA_ARGS__) + +#define __SPX_TRACE_ERROR(title, fileName, lineNumber, msg, ...) \ + __AZAC_TRACE_ERROR(title, fileName, lineNumber, msg, ##__VA_ARGS__) + +#define __SPX_TRACE_ERROR_IF(cond, title, fileName, lineNumber, msg, ...) \ + __AZAC_TRACE_ERROR_IF(cond, title, fileName, lineNumber, msg, ##__VA_ARGS__) + +#define __SPX_TRACE_VERBOSE(title, fileName, lineNumber, msg, ...) \ + __AZAC_TRACE_VERBOSE(title, fileName, lineNumber, msg, ##__VA_ARGS__) + +#define __SPX_TRACE_VERBOSE_IF(cond, title, fileName, lineNumber, msg, ...) \ + __AZAC_TRACE_VERBOSE_IF(cond, title, fileName, lineNumber, msg, ##__VA_ARGS__) + +#define ___SPX_EXPR_AS_STRING(_String) \ + ___AZAC_EXPR_AS_STRING(_String) + +#define __SPX_EXPR_AS_STRING(_String) \ + __AZAC_EXPR_AS_STRING(_String) + +#define __SPX_TRACE_HR(title, fileName, lineNumber, hr, x) \ + __AZAC_TRACE_HR(title, fileName, lineNumber, hr, x) + +#define __SPX_REPORT_ON_FAIL(title, fileName, lineNumber, hr) \ + __AZAC_REPORT_ON_FAIL(title, fileName, lineNumber, hr) + +#define __SPX_REPORT_ON_FAIL_IFNOT(title, fileName, lineNumber, hr, hrNot) \ + __AZAC_REPORT_ON_FAIL_IFNOT(title, fileName, lineNumber, hr, hrNot) + +#define __SPX_T_RETURN_HR(title, fileName, lineNumber, hr) \ + __AZAC_T_RETURN_HR(title, fileName, lineNumber, hr) + +#define __SPX_T_RETURN_HR_IF(title, fileName, lineNumber, hr, cond) \ + __AZAC_T_RETURN_HR_IF(title, fileName, lineNumber, hr, cond) + +#define __SPX_T_RETURN_ON_FAIL(title, fileName, lineNumber, hr) \ + __AZAC_T_RETURN_ON_FAIL(title, fileName, lineNumber, hr) + +#define __SPX_T_RETURN_ON_FAIL_IF_NOT(title, fileName, lineNumber, hr, hrNot) \ + __AZAC_T_RETURN_ON_FAIL_IF_NOT(title, fileName, lineNumber, hr, hrNot) + +#define __SPX_RETURN_HR(hr) \ + __AZAC_RETURN_HR(hr) + +#define __SPX_RETURN_HR_IF(hr, cond) \ + __AZAC_RETURN_HR_IF(hr, cond) + +#define __SPX_RETURN_ON_FAIL(hr) \ + __AZAC_RETURN_ON_FAIL(hr) + +#define __SPX_RETURN_ON_FAIL_IF_NOT(hr, hrNot) \ + __AZAC_RETURN_ON_FAIL_IF_NOT(hr, hrNot) + +#define SPX_EXITFN_CLEANUP AZAC_EXITFN_CLEANUP + +#define __SPX_T_EXITFN_HR(title, fileName, lineNumber, hr) \ + __AZAC_T_EXITFN_HR(title, fileName, lineNumber, hr) + +#define __SPX_T_EXITFN_HR_IF(title, fileName, lineNumber, hr, cond) \ + __AZAC_T_EXITFN_HR_IF(title, fileName, lineNumber, hr, cond) + +#define __SPX_T_EXITFN_ON_FAIL(title, fileName, lineNumber, hr) \ + __AZAC_T_EXITFN_ON_FAIL(title, fileName, lineNumber, hr) + +#define __SPX_T_EXITFN_ON_FAIL_IF_NOT(title, fileName, lineNumber, hr, hrNot) \ + __AZAC_T_EXITFN_ON_FAIL_IF_NOT(title, fileName, lineNumber, hr, hrNot) + +#define __SPX_EXITFN_HR(hr) \ + __AZAC_EXITFN_HR(hr) + +#define __SPX_EXITFN_HR_IF(hr, cond) \ + __AZAC_EXITFN_HR_IF(hr, cond) + +#define __SPX_EXITFN_ON_FAIL(hr) \ + __AZAC_EXITFN_ON_FAIL(hr) + +#define __SPX_EXITFN_ON_FAIL_IF_NOT(hr, hrNot) \ + __AZAC_EXITFN_ON_FAIL_IF_NOT(hr, hrNot) + +#define __SPX_TRACE_ASSERT(title, fileName, lineNumber, expr) \ + __AZAC_TRACE_ASSERT(title, fileName, lineNumber, expr) + +#define __SPX_TRACE_ASSERT_MSG(title, fileName, lineNumber, expr, ...) \ + __AZAC_TRACE_ASSERT_MSG(title, fileName, lineNumber, expr, ##__VA_ARGS__) + +#define __SPX_DBG_ASSERT(title, fileName, lineNumber, expr) \ + __AZAC_DBG_ASSERT(title, fileName, lineNumber, expr) + +#define __SPX_DBG_ASSERT_WITH_MESSAGE(title, fileName, lineNumber, expr, ...) \ + __AZAC_DBG_ASSERT_WITH_MESSAGE(title, fileName, lineNumber, expr, ##__VA_ARGS__) + +#define __SPX_DBG_VERIFY(title, fileName, lineNumber, expr) \ + __AZAC_DBG_VERIFY(title, fileName, lineNumber, expr) + +#define __SPX_DBG_VERIFY_WITH_MESSAGE(title, fileName, lineNumber, expr, ...) \ + __AZAC_DBG_VERIFY_WITH_MESSAGE(title, fileName, lineNumber, expr, ##__VA_ARGS__) + +#ifdef __cplusplus + +#define __SPX_TRACE_SCOPE(t1, fileName, lineNumber, t2, x, y) \ + __AZAC_TRACE_SCOPE(t1, fileName, lineNumber, t2, x, y) + +#ifndef __SPX_THROW_HR +#define __SPX_THROW_HR(hr) __SPX_THROW_HR_IMPL(hr) +#endif + +#define __SPX_T_THROW_ON_FAIL(title, fileName, lineNumber, hr) \ + __AZAC_T_THROW_ON_FAIL(title, fileName, lineNumber, hr) + +#define __SPX_T_THROW_ON_FAIL_IF_NOT(title, fileName, lineNumber, hr, hrNot) \ + __AZAC_T_THROW_ON_FAIL_IF_NOT(title, fileName, lineNumber, hr, hrNot) + +#define __SPX_T_THROW_HR_IF(title, fileName, lineNumber, hr, cond) \ + __AZAC_T_THROW_HR_IF(title, fileName, lineNumber, hr, cond) + +#define __SPX_T_THROW_HR(title, fileName, lineNumber, hr) \ + __AZAC_T_THROW_HR(title, fileName, lineNumber, hr) + +#define __SPX_THROW_ON_FAIL(hr) \ + __AZAC_THROW_ON_FAIL(hr) + +#define __SPX_THROW_ON_FAIL_IF_NOT(hr, hrNot) \ + __AZAC_THROW_ON_FAIL_IF_NOT(hr, hrNot) + +#define __SPX_THROW_HR_IF(hr, cond) \ + __AZAC_THROW_HR_IF(hr, cond) + +#endif // __cplusplus + + +//------------------------------------------------------- +// SPX_ macro definitions +//------------------------------------------------------- + +#ifdef SPX_CONFIG_TRACE_VERBOSE +#define SPX_TRACE_VERBOSE(msg, ...) __SPX_TRACE_VERBOSE("SPX_TRACE_VERBOSE: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define SPX_TRACE_VERBOSE_IF(cond, msg, ...) __SPX_TRACE_VERBOSE_IF(cond, "SPX_TRACE_VERBOSE: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#else +#define SPX_TRACE_VERBOSE(...) +#define SPX_TRACE_VERBOSE_IF(...) +#endif + +#ifdef SPX_CONFIG_DBG_TRACE_VERBOSE +#define SPX_DBG_TRACE_VERBOSE(msg, ...) __SPX_TRACE_VERBOSE("SPX_DBG_TRACE_VERBOSE: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define SPX_DBG_TRACE_VERBOSE_IF(cond, msg, ...) __SPX_TRACE_VERBOSE_IF(cond, "SPX_DBG_TRACE_VERBOSE: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#else +#define SPX_DBG_TRACE_VERBOSE(...) +#define SPX_DBG_TRACE_VERBOSE_IF(...) +#endif + +#ifdef SPX_CONFIG_TRACE_INFO +#define SPX_TRACE_INFO(msg, ...) __SPX_TRACE_INFO("SPX_TRACE_INFO: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define SPX_TRACE_INFO_IF(cond, msg, ...) __SPX_TRACE_INFO_IF(cond, "SPX_TRACE_INFO: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#else +#define SPX_TRACE_INFO(...) +#define SPX_TRACE_INFO_IF(...) +#endif + +#ifdef SPX_CONFIG_DBG_TRACE_INFO +#define SPX_DBG_TRACE_INFO(msg, ...) __SPX_TRACE_INFO("SPX_DBG_TRACE_INFO: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define SPX_DBG_TRACE_INFO_IF(cond, msg, ...) __SPX_TRACE_INFO_IF(cond, "SPX_DBG_TRACE_INFO: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#else +#define SPX_DBG_TRACE_INFO(...) +#define SPX_DBG_TRACE_INFO_IF(...) +#endif + +#ifdef SPX_CONFIG_TRACE_WARNING +#define SPX_TRACE_WARNING(msg, ...) __SPX_TRACE_WARNING("SPX_TRACE_WARNING:", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define SPX_TRACE_WARNING_IF(cond, msg, ...) __SPX_TRACE_WARNING_IF(cond, "SPX_TRACE_WARNING:", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#else +#define SPX_TRACE_WARNING(...) +#define SPX_TRACE_WARNING_IF(...) +#endif + +#ifdef SPX_CONFIG_DBG_TRACE_WARNING +#define SPX_DBG_TRACE_WARNING(msg, ...) __SPX_TRACE_WARNING("SPX_DBG_TRACE_WARNING:", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define SPX_DBG_TRACE_WARNING_IF(cond, msg, ...) __SPX_TRACE_WARNING_IF(cond, "SPX_DBG_TRACE_WARNING:", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#else +#define SPX_DBG_TRACE_WARNING(...) +#define SPX_DBG_TRACE_WARNING_IF(...) +#endif + +#ifdef SPX_CONFIG_TRACE_ERROR +#define SPX_TRACE_ERROR(msg, ...) __SPX_TRACE_ERROR("SPX_TRACE_ERROR: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define SPX_TRACE_ERROR_IF(cond, msg, ...) __SPX_TRACE_ERROR_IF(cond, "SPX_TRACE_ERROR: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#else +#define SPX_TRACE_ERROR(...) +#define SPX_TRACE_ERROR_IF(...) +#endif + +#ifdef SPX_CONFIG_DBG_TRACE_ERROR +#define SPX_DBG_TRACE_ERROR(msg, ...) __SPX_TRACE_ERROR("SPX_DBG_TRACE_ERROR: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define SPX_DBG_TRACE_ERROR_IF(cond, msg, ...) __SPX_TRACE_ERROR_IF(cond, "SPX_DBG_TRACE_ERROR: ", __FILE__, __LINE__, msg, ##__VA_ARGS__) +#else +#define SPX_DBG_TRACE_ERROR(...) +#define SPX_DBG_TRACE_ERROR_IF(...) +#endif + +#ifdef SPX_CONFIG_TRACE_FUNCTION +#define SPX_TRACE_FUNCTION(...) __SPX_TRACE_VERBOSE("SPX_TRACE_FUNCTION: ", __FILE__, __LINE__, __FUNCTION__) +#else +#define SPX_TRACE_FUNCTION(...) +#endif + +#ifdef SPX_CONFIG_DBG_TRACE_FUNCTION +#define SPX_DBG_TRACE_FUNCTION(...) __SPX_TRACE_VERBOSE("SPX_DBG_TRACE_FUNCTION: ", __FILE__, __LINE__, __FUNCTION__) +#else +#define SPX_DBG_TRACE_FUNCTION(...) +#endif + +#ifdef SPX_CONFIG_TRACE_REPORT_ON_FAIL +#define SPX_REPORT_ON_FAIL(hr) __SPX_REPORT_ON_FAIL("SPX_REPORT_ON_FAIL: ", __FILE__, __LINE__, hr) +#define SPX_REPORT_ON_FAIL_IFNOT(hr, hrNot) __SPX_REPORT_ON_FAIL_IFNOT("SPX_REPORT_ON_FAIL: ", __FILE__, __LINE__, hr, hrNot) +#else +#define SPX_REPORT_ON_FAIL(hr) UNUSED(hr) +#define SPX_REPORT_ON_FAIL_IFNOT(hr, hrNot) UNUSED(hr); UNUSED(hrNot) +#endif + +#ifdef SPX_CONFIG_TRACE_RETURN_ON_FAIL +#define SPX_RETURN_HR(hr) __SPX_T_RETURN_HR("SPX_RETURN_ON_FAIL: ", __FILE__, __LINE__, hr) +#define SPX_RETURN_HR_IF(hr, cond) __SPX_T_RETURN_HR_IF("SPX_RETURN_ON_FAIL: ", __FILE__, __LINE__, hr, cond) +#define SPX_RETURN_ON_FAIL(hr) __SPX_T_RETURN_ON_FAIL("SPX_RETURN_ON_FAIL: ", __FILE__, __LINE__, hr) +#define SPX_RETURN_ON_FAIL_IF_NOT(hr, hrNot) __SPX_T_RETURN_ON_FAIL_IF_NOT("SPX_RETURN_ON_FAIL: ", __FILE__, __LINE__, hr, hrNot) +#else +#define SPX_RETURN_HR(hr) __SPX_RETURN_HR(hr) +#define SPX_RETURN_HR_IF(hr, cond) __SPX_RETURN_HR_IF(hr, cond) +#define SPX_RETURN_ON_FAIL(hr) __SPX_RETURN_ON_FAIL(hr) +#define SPX_RETURN_ON_FAIL_IF_NOT(hr, hrNot) __SPX_RETURN_ON_FAIL_IF_NOT(hr, hrNot) +#endif + +#define SPX_IFTRUE_RETURN_HR(cond, hr) SPX_RETURN_HR_IF(hr, cond) +#define SPX_IFFALSE_RETURN_HR(cond, hr) SPX_RETURN_HR_IF(hr, !(cond)) +#define SPX_IFFAILED_RETURN_HR(hr) SPX_RETURN_ON_FAIL(hr) +#define SPX_IFFAILED_RETURN_HR_IFNOT(hr, hrNot) SPX_RETURN_ON_FAIL_IF_NOT(hr, hrNot) + +#ifdef SPX_CONFIG_TRACE_EXITFN_ON_FAIL +#define SPX_EXITFN_HR(hr) __SPX_T_EXITFN_HR("SPX_EXITFN_ON_FAIL: ", __FILE__, __LINE__, hr) +#define SPX_EXITFN_HR_IF(hr, cond) __SPX_T_EXITFN_HR_IF("SPX_EXITFN_ON_FAIL: ", __FILE__, __LINE__, hr, cond) +#define SPX_EXITFN_ON_FAIL(hr) __SPX_T_EXITFN_ON_FAIL("SPX_EXITFN_ON_FAIL: ", __FILE__, __LINE__, hr) +#define SPX_EXITFN_ON_FAIL_IF_NOT(hr, hrNot) __SPX_T_EXITFN_ON_FAIL_IF_NOT("SPX_EXITFN_ON_FAIL: ", __FILE__, __LINE__, hr, hrNot) +#else +#define SPX_EXITFN_HR(hr) __SPX_EXITFN_HR(hr) +#define SPX_EXITFN_HR_IF(hr, cond) __SPX_EXITFN_HR_IF(hr, cond) +#define SPX_EXITFN_ON_FAIL(hr) __SPX_EXITFN_ON_FAIL(hr) +#define SPX_EXITFN_ON_FAIL_IF_NOT(hr, hrNot) __SPX_EXITFN_ON_FAIL_IF_NOT(hr, hrNot) +#endif + +#define SPX_IFTRUE_EXITFN_WHR(cond, hr) SPX_EXITFN_HR_IF(hr, cond) +#define SPX_IFFALSE_EXITFN_WHR(cond, hr) SPX_EXITFN_HR_IF(hr, !(cond)) +#define SPX_IFFAILED_EXITFN_WHR(hr) SPX_EXITFN_ON_FAIL(hr) +#define SPX_IFFAILED_EXITFN_WHR_IFNOT(hr, hrNot) SPX_EXITFN_ON_FAIL_IF_NOT(hr, hrNot) + +#define SPX_IFTRUE_EXITFN_CLEANUP(cond, expr) AZAC_IFTRUE_EXITFN_CLEANUP(cond, expr) +#define SPX_IFFALSE_EXITFN_CLEANUP(cond, expr) AZAC_IFFALSE_EXITFN_CLEANUP(cond, expr) + +#if defined(SPX_CONFIG_DBG_TRACE_ASSERT) && (defined(DEBUG) || defined(_DEBUG)) +#define SPX_DBG_ASSERT(expr) __SPX_DBG_ASSERT("SPX_ASSERT: ", __FILE__, __LINE__, expr) +#define SPX_DBG_ASSERT_WITH_MESSAGE(expr, ...) __SPX_DBG_ASSERT_WITH_MESSAGE("SPX_ASSERT: ", __FILE__, __LINE__, expr, ##__VA_ARGS__) +#else +#define SPX_DBG_ASSERT(expr) +#define SPX_DBG_ASSERT_WITH_MESSAGE(expr, ...) +#endif + +#if defined(SPX_CONFIG_DBG_TRACE_VERIFY) && (defined(DEBUG) || defined(_DEBUG)) +#define SPX_DBG_VERIFY(expr) __SPX_DBG_VERIFY("SPX_VERIFY: ", __FILE__, __LINE__, expr) +#define SPX_DBG_VERIFY_WITH_MESSAGE(expr, ...) __SPX_DBG_VERIFY_WITH_MESSAGE("SPX_VERIFY: ", __FILE__, __LINE__, expr, ##__VA_ARGS__) +#else +#define SPX_DBG_VERIFY(expr) (expr) +#define SPX_DBG_VERIFY_WITH_MESSAGE(expr, ...) (expr) +#endif + +#define SPX_IFTRUE(cond, expr) AZAC_IFTRUE(cond, expr) +#define SPX_IFFALSE(cond, expr) AZAC_IFFALSE(cond, expr) + +#ifdef __cplusplus + +#ifdef SPX_CONFIG_TRACE_SCOPE +#define SPX_TRACE_SCOPE(x, y) __SPX_TRACE_SCOPE("SPX_TRACE_SCOPE_ENTER: ", __FILE__, __LINE__, "SPX_TRACE_SCOPE_EXIT: ", x, y) +#else +#define SPX_TRACE_SCOPE(x, y) +#endif + +#ifdef SPX_CONFIG_DBG_TRACE_SCOPE +#define SPX_DBG_TRACE_SCOPE(x, y) __SPX_TRACE_SCOPE("SPX_DBG_TRACE_SCOPE_ENTER: ", __FILE__, __LINE__, "SPX_DBG_TRACE_SCOPE_EXIT: ", x, y) +#else +#define SPX_DBG_TRACE_SCOPE(x, y) +#endif + +#ifdef SPX_CONFIG_TRACE_THROW_ON_FAIL +#define SPX_THROW_ON_FAIL(hr) __SPX_T_THROW_ON_FAIL("SPX_THROW_ON_FAIL: ", __FILE__, __LINE__, hr) +#define SPX_THROW_ON_FAIL_IF_NOT(hr, hrNot) __SPX_T_THROW_ON_FAIL_IF_NOT("SPX_THROW_ON_FAIL: ", __FILE__, __LINE__, hr, hrNot) +#define SPX_THROW_HR_IF(hr, cond) __SPX_T_THROW_HR_IF("SPX_THROW_HR_IF: ", __FILE__, __LINE__, hr, cond) +#define SPX_THROW_HR(hr) __SPX_T_THROW_HR("SPX_THROW_HR: ", __FILE__, __LINE__, hr) +#else +#define SPX_THROW_ON_FAIL(hr) __SPX_THROW_ON_FAIL(hr) +#define SPX_THROW_ON_FAIL_IF_NOT(hr, hrNot) __SPX_THROW_ON_FAIL_IF_NOT(hr, hrNot) +#define SPX_THROW_HR_IF(hr, cond) __SPX_THROW_HR_IF(hr, cond) +#define SPX_THROW_HR(hr) __SPX_THROW_HR(hr) +#endif + +#define SPX_IFFAILED_THROW_HR(hr) SPX_THROW_ON_FAIL(hr) +#define SPX_IFFAILED_THROW_HR_IFNOT(hr, hrNot) SPX_THROW_ON_FAIL_IF_NOT(hr, hrNot) + +#else // __cplusplus + +#define SPX_TRACE_SCOPE(x, y) static_assert(false) +#define SPX_DBG_TRACE_SCOPE(x, y) static_assert(false) +#define SPX_THROW_ON_FAIL(hr) static_assert(false) +#define SPX_THROW_ON_FAIL_IF_NOT(hr, hrNot) static_assert(false) +#define SPX_THROW_HR_IF(hr, cond) static_assert(false) +#define SPX_THROW_HR(hr) static_assert(false) +#define SPX_IFFAILED_THROW_HR(hr) static_assert(false) +#define SPX_IFFAILED_THROW_HR_IFNOT(hr, hrNot) static_assert(false) + +#endif // __cplusplus diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/spxerror.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/spxerror.h new file mode 100644 index 0000000..760ec06 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/c_api/spxerror.h @@ -0,0 +1,449 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once + +#include // must include after spxdebug.h or speechapi*.h (can NOT be included before) + +#define SPXHR AZACHR +#define SPX_NOERROR AZAC_ERR_NONE +#define SPX_INIT_HR(hr) AZAC_INIT_HR(hr) +#define SPX_SUCCEEDED(x) AZAC_SUCCEEDED(x) +#define SPX_FAILED(x) AZAC_FAILED(x) +#define __SPX_ERRCODE_FAILED(x) __AZAC_ERRCODE_FAILED(x) + +/// +/// The function is not implemented. +/// +#define SPXERR_NOT_IMPL \ + AZAC_ERR_NOT_IMPL + +/// +/// The object has not been properly initialized. +/// +#define SPXERR_UNINITIALIZED \ + AZAC_ERR_UNINITIALIZED + +/// +/// The object has already been initialized. +/// +#define SPXERR_ALREADY_INITIALIZED \ + AZAC_ERR_ALREADY_INITIALIZED + +/// +/// An unhandled exception was detected. +/// +#define SPXERR_UNHANDLED_EXCEPTION \ + AZAC_ERR_UNHANDLED_EXCEPTION + +/// +/// The object or property was not found. +/// +#define SPXERR_NOT_FOUND \ + AZAC_ERR_NOT_FOUND + +/// +/// One or more arguments are not valid. +/// +#define SPXERR_INVALID_ARG \ + AZAC_ERR_INVALID_ARG + +/// +/// The specified timeout value has elapsed. +/// +#define SPXERR_TIMEOUT \ + AZAC_ERR_TIMEOUT + +/// +/// The asynchronous operation is already in progress. +/// +#define SPXERR_ALREADY_IN_PROGRESS \ + AZAC_ERR_ALREADY_IN_PROGRESS + +/// +/// The attempt to open the file failed. +/// +#define SPXERR_FILE_OPEN_FAILED \ + AZAC_ERR_FILE_OPEN_FAILED + +/// +/// The end of the file was reached unexpectedly. +/// +#define SPXERR_UNEXPECTED_EOF \ + AZAC_ERR_UNEXPECTED_EOF + +/// +/// Invalid audio header encountered. +/// +#define SPXERR_INVALID_HEADER \ + AZAC_ERR_INVALID_HEADER + +/// +/// The requested operation cannot be performed while audio is pumping +/// +#define SPXERR_AUDIO_IS_PUMPING \ + AZAC_ERR_AUDIO_IS_PUMPING + +/// +/// Unsupported audio format. +/// +#define SPXERR_UNSUPPORTED_FORMAT \ + AZAC_ERR_UNSUPPORTED_FORMAT + +/// +/// Operation aborted. +/// +#define SPXERR_ABORT \ + AZAC_ERR_ABORT + +/// +/// Microphone is not available. +/// +#define SPXERR_MIC_NOT_AVAILABLE \ + AZAC_ERR_MIC_NOT_AVAILABLE + +/// +/// An invalid state was encountered. +/// +#define SPXERR_INVALID_STATE \ + AZAC_ERR_INVALID_STATE + +/// +/// Attempting to create a UUID failed. +/// +#define SPXERR_UUID_CREATE_FAILED \ + AZAC_ERR_UUID_CREATE_FAILED + +/// +/// An unexpected session state transition was encountered when setting the session audio format. +/// +/// +/// Valid transitions are: +/// * WaitForPumpSetFormatStart --> ProcessingAudio (at the beginning of stream) +/// * StoppingPump --> WaitForAdapterCompletedSetFormatStop (at the end of stream) +/// * ProcessingAudio --> WaitForAdapterCompletedSetFormatStop (when the stream runs out of data) +/// All other state transitions are invalid. +/// +#define SPXERR_SETFORMAT_UNEXPECTED_STATE_TRANSITION \ + AZAC_ERR_SETFORMAT_UNEXPECTED_STATE_TRANSITION + +/// +/// An unexpected session state was encountered in while processing audio. +/// +/// +/// Valid states to encounter are: +/// * ProcessingAudio: We're allowed to process audio while in this state. +/// * StoppingPump: We're allowed to be called to process audio, but we'll ignore the data passed in while we're attempting to stop the pump. +/// All other states are invalid while processing audio. +/// +#define SPXERR_PROCESS_AUDIO_INVALID_STATE \ + AZAC_ERR_PROCESS_AUDIO_INVALID_STATE + +/// +/// An unexpected state transition was encountered while attempting to start recognizing. +/// +/// +/// A valid transition is: +/// * Idle --> WaitForPumpSetFormatStart +/// All other state transitions are invalid when attempting to start recognizing +/// +#define SPXERR_START_RECOGNIZING_INVALID_STATE_TRANSITION \ + AZAC_ERR_START_RECOGNIZING_INVALID_STATE_TRANSITION + +/// +/// An unexpected error was encountered when trying to create an internal object. +/// +#define SPXERR_UNEXPECTED_CREATE_OBJECT_FAILURE \ + AZAC_ERR_UNEXPECTED_CREATE_OBJECT_FAILURE + +/// +/// An error in the audio-capturing system. +/// +#define SPXERR_MIC_ERROR \ + AZAC_ERR_MIC_ERROR + +/// +/// The requested operation cannot be performed; there is no audio input. +/// +#define SPXERR_NO_AUDIO_INPUT \ + AZAC_ERR_NO_AUDIO_INPUT + +/// +/// An unexpected error was encountered when trying to access the USP site. +/// +#define SPXERR_UNEXPECTED_USP_SITE_FAILURE \ + AZAC_ERR_UNEXPECTED_USP_SITE_FAILURE + +/// +/// An unexpected error was encountered when trying to access the LuAdapterSite site. +/// +#define SPXERR_UNEXPECTED_LU_SITE_FAILURE \ + AZAC_ERR_UNEXPECTED_LU_SITE_FAILURE + +/// +/// The buffer is too small. +/// +#define SPXERR_BUFFER_TOO_SMALL \ + AZAC_ERR_BUFFER_TOO_SMALL + +/// +/// A method failed to allocate memory. +/// +#define SPXERR_OUT_OF_MEMORY \ + AZAC_ERR_OUT_OF_MEMORY + +/// +/// An unexpected runtime error occurred. +/// +#define SPXERR_RUNTIME_ERROR \ + AZAC_ERR_RUNTIME_ERROR + +/// +/// The url specified is invalid. +/// +#define SPXERR_INVALID_URL \ + AZAC_ERR_INVALID_URL + +/// +/// The region specified is invalid or missing. +/// +#define SPXERR_INVALID_REGION \ + AZAC_ERR_INVALID_REGION + +/// +/// Switch between single shot and continuous recognition is not supported. +/// +#define SPXERR_SWITCH_MODE_NOT_ALLOWED \ + AZAC_ERR_SWITCH_MODE_NOT_ALLOWED + +/// +/// Changing connection status is not supported in the current recognition state. +/// +#define SPXERR_CHANGE_CONNECTION_STATUS_NOT_ALLOWED \ + AZAC_ERR_CHANGE_CONNECTION_STATUS_NOT_ALLOWED + +/// +/// Explicit connection management is not supported by the specified recognizer. +/// +#define SPXERR_EXPLICIT_CONNECTION_NOT_SUPPORTED_BY_RECOGNIZER \ + AZAC_ERR_EXPLICIT_CONNECTION_NOT_SUPPORTED_BY_RECOGNIZER + +/// +/// The handle is invalid. +/// +#define SPXERR_INVALID_HANDLE \ + AZAC_ERR_INVALID_HANDLE + +/// +/// The recognizer is invalid. +/// +#define SPXERR_INVALID_RECOGNIZER \ + AZAC_ERR_INVALID_RECOGNIZER + +/// +/// The value is out of range. +/// Added in version 1.3.0. +/// +#define SPXERR_OUT_OF_RANGE \ + AZAC_ERR_OUT_OF_RANGE + +/// +/// Extension library not found. +/// Added in version 1.3.0. +/// +#define SPXERR_EXTENSION_LIBRARY_NOT_FOUND \ + AZAC_ERR_EXTENSION_LIBRARY_NOT_FOUND + +/// +/// An unexpected error was encountered when trying to access the TTS engine site. +/// Added in version 1.4.0. +/// +#define SPXERR_UNEXPECTED_TTS_ENGINE_SITE_FAILURE \ + AZAC_ERR_UNEXPECTED_TTS_ENGINE_SITE_FAILURE + +/// +/// An unexpected error was encountered when trying to access the audio output stream. +/// Added in version 1.4.0. +/// +#define SPXERR_UNEXPECTED_AUDIO_OUTPUT_FAILURE \ + AZAC_ERR_UNEXPECTED_AUDIO_OUTPUT_FAILURE + +/// +/// Gstreamer internal error. +/// Added in version 1.4.0. +/// +#define SPXERR_GSTREAMER_INTERNAL_ERROR \ + AZAC_ERR_GSTREAMER_INTERNAL_ERROR + +/// +/// Compressed container format not supported. +/// Added in version 1.4.0. +/// +#define SPXERR_CONTAINER_FORMAT_NOT_SUPPORTED_ERROR \ + AZAC_ERR_CONTAINER_FORMAT_NOT_SUPPORTED_ERROR + +/// +/// Codec extension or gstreamer not found. +/// Added in version 1.4.0. +/// +#define SPXERR_GSTREAMER_NOT_FOUND_ERROR \ + AZAC_ERR_GSTREAMER_NOT_FOUND_ERROR + +/// +/// The language specified is missing. +/// Added in version 1.5.0. +/// +#define SPXERR_INVALID_LANGUAGE \ + AZAC_ERR_INVALID_LANGUAGE + +/// +/// The API is not applicable. +/// Added in version 1.5.0. +/// +#define SPXERR_UNSUPPORTED_API_ERROR \ + AZAC_ERR_UNSUPPORTED_API_ERROR + +/// +/// The ring buffer is unavailable. +/// Added in version 1.8.0. +/// +#define SPXERR_RINGBUFFER_DATA_UNAVAILABLE \ + AZAC_ERR_RINGBUFFER_DATA_UNAVAILABLE + +/// +/// An unexpected error was encountered when trying to access the Conversation site. +/// Added in version 1.5.0. +/// +#define SPXERR_UNEXPECTED_CONVERSATION_SITE_FAILURE \ + AZAC_ERR_UNEXPECTED_CONVERSATION_SITE_FAILURE + +/// +/// An unexpected error was encountered when trying to access the Conversation site. +/// Added in version 1.8.0. +/// +#define SPXERR_UNEXPECTED_CONVERSATION_TRANSLATOR_SITE_FAILURE \ + AZAC_ERR_UNEXPECTED_CONVERSATION_TRANSLATOR_SITE_FAILURE + +/// +/// An asynchronous operation was canceled before it was executed. +/// Added in version 1.8.0. +/// +#define SPXERR_CANCELED \ + AZAC_ERR_CANCELED + +/// +/// Codec for compression could not be initialized. +/// Added in version 1.10.0. +/// +#define SPXERR_COMPRESS_AUDIO_CODEC_INITIFAILED \ + AZAC_ERR_COMPRESS_AUDIO_CODEC_INITIFAILED + +/// +/// Data not available. +/// Added in version 1.10.0. +/// +#define SPXERR_DATA_NOT_AVAILABLE \ + AZAC_ERR_DATA_NOT_AVAILABLE + +/// +/// Invalid result reason. +/// Added in version 1.12.0 +/// +#define SPXERR_INVALID_RESULT_REASON \ + AZAC_ERR_INVALID_RESULT_REASON + +/// +/// An unexpected error was encountered when trying to access the RNN-T site. +/// +#define SPXERR_UNEXPECTED_RNNT_SITE_FAILURE \ + AZAC_ERR_UNEXPECTED_RNNT_SITE_FAILURE + +/// +/// Sending of a network message failed. +/// +#define SPXERR_NETWORK_SEND_FAILED \ + AZAC_ERR_NETWORK_SEND_FAILED + +/// +/// Audio extension library not found. +/// Added in version 1.16.0. +/// +#define SPXERR_AUDIO_SYS_LIBRARY_NOT_FOUND \ + AZAC_ERR_AUDIO_SYS_LIBRARY_NOT_FOUND + +/// +/// An error in the audio-rendering system. +/// Added in version 1.20.0 +/// +#define SPXERR_LOUDSPEAKER_ERROR \ + AZAC_ERR_LOUDSPEAKER_ERROR + +/// +/// An unexpected error was encountered when trying to access the Vision site. +/// Added in version 1.15.0. +/// +#define SPXERR_VISION_SITE_FAILURE \ + AZAC_ERR_VISION_SITE_FAILURE + +/// +/// Stream number provided was invalid in the current context. +/// Added in version 1.15.0. +/// +#define SPXERR_MEDIA_INVALID_STREAM \ + AZAC_ERR_MEDIA_INVALID_STREAM + +/// +/// Offset required is invalid in the current context. +/// Added in version 1.15.0. +/// +#define SPXERR_MEDIA_INVALID_OFFSET \ + AZAC_ERR_MEDIA_INVALID_OFFSET + +/// +/// No more data is available in source. +/// Added in version 1.15.0. +/// +#define SPXERR_MEDIA_NO_MORE_DATA \ + AZAC_ERR_MEDIA_NO_MORE_DATA + +/// +/// Source has not been started. +/// Added in version 1.15.0. +/// +#define SPXERR_MEDIA_NOT_STARTED \ + AZAC_ERR_MEDIA_NOT_STARTED + +/// +/// Source has already been started. +/// Added in version 1.15.0. +/// +#define SPXERR_MEDIA_ALREADY_STARTED \ + AZAC_ERR_MEDIA_ALREADY_STARTED + +/// +/// Media device creation failed. +/// Added in version 1.18.0. +/// +#define SPXERR_MEDIA_DEVICE_CREATION_FAILED \ + AZAC_ERR_MEDIA_DEVICE_CREATION_FAILED + +/// +/// No devices of the selected category are available. +/// Added in version 1.18.0. +/// +#define SPXERR_MEDIA_NO_DEVICE_AVAILABLE \ + AZAC_ERR_MEDIA_NO_DEVICE_AVAILABLE + +/// +/// Enabled Voice Activity Detection while using keyword recognition is not allowed. +/// +#define SPXERR_VAD_COULD_NOT_USE_WITH_KEYWORD_RECOGNIZER \ + AZAC_ERR_VAD_COULD_NOT_USE_WITH_KEYWORD_RECOGNIZER + +/// +/// The specified RecoEngineAdapter could not be created. +/// +#define SPXERR_COULD_NOT_CREATE_ENGINE_ADAPTER \ + AZAC_ERR_COULD_NOT_CREATE_ENGINE_ADAPTER diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/CMakeLists.txt b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/CMakeLists.txt new file mode 100644 index 0000000..c5ee7ae --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.19) + +project(cxx_headers) + +set(SRC_DIR "${PROJECT_SOURCE_DIR}") +add_library(${PROJECT_NAME} INTERFACE ${SPEECH_CXX_API_HEADERS}) +target_include_directories(${PROJECT_NAME} INTERFACE ${PROJECT_SOURCE_DIR}) +target_link_libraries(${PROJECT_NAME} INTERFACE c_headers) +set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER api) diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/azac_api_cxx_common.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/azac_api_cxx_common.h new file mode 100644 index 0000000..f097d2e --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/azac_api_cxx_common.h @@ -0,0 +1,82 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/azai/license202106 for the full license information. +// + +#pragma once + +// TODO: TFS#3671215 - Vision: C/C++ azac_api* files are in shared include directory, speech and vision share + +#include +#include +#include +#include +#include +#include +#include + +#define AZAC_DISABLE_COPY_AND_MOVE(T) \ + /** \brief Disable copy constructor */ \ + T(const T&) = delete; \ + /** \brief Disable copy assignment */ \ + T& operator=(const T&) = delete; \ + /** \brief Disable move constructor */ \ + T(T&&) = delete; \ + /** \brief Disable move assignment */ \ + T& operator=(T&&) = delete + +#define AZAC_DISABLE_DEFAULT_CTORS(T) \ + /** \brief Disable default constructor */ \ + T() = delete; \ + AZAC_DISABLE_COPY_AND_MOVE(T) + +#if defined(__GNUG__) && defined(__linux__) && !defined(ANDROID) && !defined(__ANDROID__) +#include +#define SHOULD_HANDLE_FORCED_UNWIND 1 +#endif + +/*! \cond INTERNAL */ + +namespace Azure { +namespace AI { +namespace Core { +namespace _detail { + +template +class ProtectedAccess : public T +{ +public: + + static AZAC_HANDLE HandleFromPtr(T* ptr) { + if (ptr == nullptr) + { + return nullptr; + } + auto access = static_cast(ptr); + return (AZAC_HANDLE)(*access); + } + + static AZAC_HANDLE HandleFromConstPtr(const T* ptr) { + if (ptr == nullptr) + { + return nullptr; + } + auto access = static_cast(ptr); + return (AZAC_HANDLE)(*access); + } + + template + static std::shared_ptr FromHandle(AZAC_HANDLE handle, Args... extras) { + return T::FromHandle(handle, extras...); + } + + template + static std::shared_ptr Create(Args&&... args) { + return T::Create(std::forward(args)...); + } + +}; + +} } } } // Azure::AI::Core::Details + +/*! \endcond */ diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx.h new file mode 100644 index 0000000..44fe059 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx.h @@ -0,0 +1,117 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx.h: Master include header for public C++ API declarations +// + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_config.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_config.h new file mode 100644 index 0000000..abfd623 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_config.h @@ -0,0 +1,338 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_audio_config.h: Public API declarations for AudioConfig C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Audio { + + +/// +/// Represents audio input or output configuration. Audio input can be from a microphone, file, +/// or input stream. Audio output can be to a speaker, audio file output in WAV format, or output +/// stream. +/// +class AudioConfig +{ +public: + + /// + /// Internal operator used to get underlying handle value. + /// + /// A handle. + explicit operator SPXAUDIOCONFIGHANDLE() const { return m_haudioConfig.get(); } + + /// + /// Creates an AudioConfig object representing the default microphone on the system. + /// + /// A shared pointer to the AudioConfig object + static std::shared_ptr FromDefaultMicrophoneInput() + { + SPXAUDIOCONFIGHANDLE haudioConfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_config_create_audio_input_from_default_microphone(&haudioConfig)); + + auto config = new AudioConfig(haudioConfig); + return std::shared_ptr(config); + } + + /// + /// Creates an AudioConfig object representing the default microphone on the system. + /// + /// Audio processing options. + /// A shared pointer to the AudioConfig object + static std::shared_ptr FromDefaultMicrophoneInput(std::shared_ptr audioProcessingOptions) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, audioProcessingOptions == nullptr); + + SPXAUDIOCONFIGHANDLE haudioConfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_config_create_audio_input_from_default_microphone(&haudioConfig)); + SPX_THROW_ON_FAIL(audio_config_set_audio_processing_options(haudioConfig, static_cast(*audioProcessingOptions.get()))); + + auto config = new AudioConfig(haudioConfig); + return std::shared_ptr(config); + } + + /// + /// Creates an AudioConfig object representing a specific microphone on the system. + /// Added in version 1.3.0. + /// + /// Specifies the device name. Please refer to this page on how to retrieve platform-specific microphone names. + /// A shared pointer to the AudioConfig object + static std::shared_ptr FromMicrophoneInput(const SPXSTRING& deviceName) + { + SPXAUDIOCONFIGHANDLE haudioConfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_config_create_audio_input_from_a_microphone(&haudioConfig, Utils::ToUTF8(deviceName).c_str())); + + auto config = new AudioConfig(haudioConfig); + return std::shared_ptr(config); + } + + /// + /// Creates an AudioConfig object representing a specific microphone on the system. + /// + /// Specifies the device name. Please refer to this page on how to retrieve platform-specific microphone names. + /// Audio processing options. + /// A shared pointer to the AudioConfig object + static std::shared_ptr FromMicrophoneInput(const SPXSTRING& deviceName, std::shared_ptr audioProcessingOptions) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, audioProcessingOptions == nullptr); + + SPXAUDIOCONFIGHANDLE haudioConfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_config_create_audio_input_from_a_microphone(&haudioConfig, Utils::ToUTF8(deviceName).c_str())); + SPX_THROW_ON_FAIL(audio_config_set_audio_processing_options(haudioConfig, static_cast(*audioProcessingOptions.get()))); + + auto config = new AudioConfig(haudioConfig); + return std::shared_ptr(config); + } + + /// + /// Creates an AudioConfig object representing the specified file. + /// + /// Specifies the audio input file. + /// A shared pointer to the AudioConfig object + static std::shared_ptr FromWavFileInput(const SPXSTRING& fileName) + { + SPXAUDIOCONFIGHANDLE haudioConfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_config_create_audio_input_from_wav_file_name(&haudioConfig, Utils::ToUTF8(fileName).c_str())); + + auto config = new AudioConfig(haudioConfig); + return std::shared_ptr(config); + } + + /// + /// Creates an AudioConfig object representing the specified file. + /// + /// Specifies the audio input file. + /// Audio processing options. + /// A shared pointer to the AudioConfig object + static std::shared_ptr FromWavFileInput(const SPXSTRING& fileName, std::shared_ptr audioProcessingOptions) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, audioProcessingOptions == nullptr); + + SPXAUDIOCONFIGHANDLE haudioConfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_config_create_audio_input_from_wav_file_name(&haudioConfig, Utils::ToUTF8(fileName).c_str())); + SPX_THROW_ON_FAIL(audio_config_set_audio_processing_options(haudioConfig, static_cast(*audioProcessingOptions.get()))); + + auto config = new AudioConfig(haudioConfig); + return std::shared_ptr(config); + } + + /// + /// Creates an AudioConfig object representing the specified stream. + /// + /// Specifies the custom audio input stream. + /// A shared pointer to the AudioConfig object + static std::shared_ptr FromStreamInput(std::shared_ptr stream) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, stream == nullptr); + + SPXAUDIOCONFIGHANDLE haudioConfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_config_create_audio_input_from_stream(&haudioConfig, GetStreamHandle(stream))); + + auto config = new AudioConfig(haudioConfig); + return std::shared_ptr(config); + } + + /// + /// Creates an AudioConfig object representing the specified stream. + /// + /// Specifies the custom audio input stream. + /// Audio processing options. + /// A shared pointer to the AudioConfig object + static std::shared_ptr FromStreamInput(std::shared_ptr stream, std::shared_ptr audioProcessingOptions) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, stream == nullptr); + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, audioProcessingOptions == nullptr); + + SPXAUDIOCONFIGHANDLE haudioConfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_config_create_audio_input_from_stream(&haudioConfig, GetStreamHandle(stream))); + SPX_THROW_ON_FAIL(audio_config_set_audio_processing_options(haudioConfig, static_cast(*audioProcessingOptions.get()))); + + auto config = new AudioConfig(haudioConfig); + return std::shared_ptr(config); + } + + /// + /// Creates an AudioConfig object representing the default audio output device (speaker) on the system. + /// Added in version 1.4.0 + /// + /// A shared pointer to the AudioConfig object + static std::shared_ptr FromDefaultSpeakerOutput() + { + SPXAUDIOCONFIGHANDLE haudioConfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_config_create_audio_output_from_default_speaker(&haudioConfig)); + + auto config = new AudioConfig(haudioConfig); + return std::shared_ptr(config); + } + + /// + /// Creates an AudioConfig object representing a specific speaker on the system. + /// Added in version 1.14.0. + /// + /// Specifies the device name. Please refer to this page on how to retrieve platform-specific audio device names. + /// A shared pointer to the AudioConfig object + static std::shared_ptr FromSpeakerOutput(const SPXSTRING& deviceName) + { + SPXAUDIOCONFIGHANDLE haudioConfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_config_create_audio_output_from_a_speaker(&haudioConfig, Utils::ToUTF8(deviceName).c_str())); + + auto config = new AudioConfig(haudioConfig); + return std::shared_ptr(config); + } + + /// + /// Creates an AudioConfig object representing the specified file for audio output. + /// Added in version 1.4.0 + /// + /// Specifies the audio output file. The parent directory must already exist. + /// A shared pointer to the AudioConfig object + static std::shared_ptr FromWavFileOutput(const SPXSTRING& fileName) + { + SPXAUDIOCONFIGHANDLE haudioConfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_config_create_audio_output_from_wav_file_name(&haudioConfig, Utils::ToUTF8(fileName).c_str())); + + auto config = new AudioConfig(haudioConfig); + return std::shared_ptr(config); + } + + /// + /// Creates an AudioConfig object representing the specified output stream. + /// Added in version 1.4.0 + /// + /// Specifies the custom audio output stream. + /// A shared pointer to the AudioConfig object + static std::shared_ptr FromStreamOutput(std::shared_ptr stream) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, stream == nullptr); + + SPXAUDIOCONFIGHANDLE haudioConfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_config_create_audio_output_from_stream(&haudioConfig, GetOutputStreamHandle(stream))); + + auto config = new AudioConfig(haudioConfig); + config->m_outputStream = stream; + return std::shared_ptr(config); + } + + /// + /// Sets a property value by name. + /// + /// The property name. + /// The property value. + void SetProperty(const SPXSTRING& name, const SPXSTRING& value) + { + property_bag_set_string(m_propertybag, -1, Utils::ToUTF8(name).c_str(), Utils::ToUTF8(value).c_str()); + } + + /// + /// Sets a property value by ID. + /// + /// The property id. + /// The property value. + void SetProperty(PropertyId id, const SPXSTRING& value) + { + property_bag_set_string(m_propertybag, static_cast(id), nullptr, Utils::ToUTF8(value).c_str()); + } + + /// + /// Gets a property value by name. + /// + /// The parameter name. + /// The property value. + SPXSTRING GetProperty(const SPXSTRING& name) const + { + const char* value = property_bag_get_string(m_propertybag, -1, Utils::ToUTF8(name).c_str(), ""); + return Utils::ToSPXString(Utils::CopyAndFreePropertyString(value)); + } + + /// + /// Gets a property value by ID. + /// + /// The parameter id. + /// The property value. + SPXSTRING GetProperty(PropertyId id) const + { + const char* value = property_bag_get_string(m_propertybag, static_cast(id), nullptr, ""); + return Utils::ToSPXString(Utils::CopyAndFreePropertyString(value)); + } + + /// + /// Gets an instance of AudioProcessingOptions class which contains the parameters for audio processing used by Speech SDK. + /// + /// A shared pointer to the AudioProcessingOptions object. + std::shared_ptr GetAudioProcessingOptions() const + { + SPXAUDIOPROCESSINGOPTIONSHANDLE hoptions = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_config_get_audio_processing_options(m_haudioConfig, &hoptions)); + + return std::make_shared(hoptions); + } + + /// + /// Destructs the object. + /// + virtual ~AudioConfig() + { + property_bag_release(m_propertybag); + } + +protected: + + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + explicit AudioConfig(SPXAUDIOCONFIGHANDLE haudioConfig) + : m_haudioConfig(haudioConfig) + { + SPX_THROW_ON_FAIL(audio_config_get_property_bag(m_haudioConfig, &m_propertybag)); + } + + /// + /// Internal helper method to get the audio stream format handle. + /// + static SPXAUDIOSTREAMHANDLE GetStreamHandle(std::shared_ptr stream) { return (SPXAUDIOSTREAMHANDLE)(*stream.get()); } + + /// + /// Internal helper method to get the audio output stream format handle. + /// + static SPXAUDIOSTREAMHANDLE GetOutputStreamHandle(std::shared_ptr stream) { return (SPXAUDIOSTREAMHANDLE)(*stream.get()); } + + /*! \endcond */ + +private: + + DISABLE_COPY_AND_MOVE(AudioConfig); + + /// + /// Internal member variable that holds the smart handle. + /// + SmartHandle m_haudioConfig; + + /// + /// Internal member variable that holds the properties of the audio config + /// + SPXPROPERTYBAGHANDLE m_propertybag; + + std::shared_ptr m_stream; + std::shared_ptr m_outputStream; +}; + + +} } } } // Microsoft::CognitiveServices::Speech::Audio diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_data_stream.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_data_stream.h new file mode 100644 index 0000000..de3d3b7 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_data_stream.h @@ -0,0 +1,239 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_audio_data_stream.h: Public API declarations for AudioDataStream C++ class +// + +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +class SpeechSynthesisResult; +class KeywordRecognitionResult; + +/// +/// Represents audio data stream used for operating audio data as a stream. +/// Added in version 1.4.0 +/// +class AudioDataStream : public std::enable_shared_from_this +{ +private: + + /// + /// Internal member variable that holds the smart handle. + /// + SmartHandle m_haudioStream; + + /*! \cond PRIVATE */ + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXAUDIOSTREAMHANDLE hstream) : + PropertyCollection( + [=]() { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + audio_data_stream_get_property_bag(hstream, &hpropbag); + return hpropbag; + }()) + { + } + }; + + /// + /// Internal member variable that holds the properties associating to the audio data stream. + /// + PrivatePropertyCollection m_properties; + + /*! \endcond */ + +public: + + /// + /// Destroy the instance. + /// + ~AudioDataStream() + { + DetachInput(); + } + + /// + /// Creates a memory backed AudioDataStream for the specified audio input file. + /// Added in version 1.14.0 + /// + /// Specifies the audio input file. + /// A shared pointer to AudioDataStream + static std::shared_ptr FromWavFileInput(const SPXSTRING& fileName) + { + SPXAUDIOSTREAMHANDLE hstream = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_data_stream_create_from_file(&hstream, Utils::ToUTF8(fileName).c_str())); + + auto stream = new AudioDataStream(hstream); + return std::shared_ptr(stream); + } + + /// + /// Creates a memory backed AudioDataStream from given speech synthesis result. + /// + /// The speech synthesis result. + /// A shared pointer to AudioDataStream + static std::shared_ptr FromResult(std::shared_ptr result); + + /// + /// Obtains the memory backed AudioDataStream associated with a given KeywordRecognition result. + /// + /// The keyword recognition result. + /// An audio stream with the input to the KeywordRecognizer starting from right before the Keyword. + static std::shared_ptr FromResult(std::shared_ptr result); + + /// + /// Get current status of the audio data stream. + /// + /// Current status + StreamStatus GetStatus() + { + Stream_Status status = StreamStatus_Unknown; + SPX_THROW_ON_FAIL(audio_data_stream_get_status(m_haudioStream, &status)); + return (StreamStatus)status; + } + + /// + /// Check whether the stream has enough data to be read. + /// + /// The requested data size in bytes. + /// A bool indicating whether the stream has enough data to be read. + bool CanReadData(uint32_t bytesRequested) + { + return audio_data_stream_can_read_data(m_haudioStream, bytesRequested); + } + + /// + /// Check whether the stream has enough data to be read, starting from the specified position. + /// + /// The position counting from start of the stream. + /// The requested data size in bytes. + /// A bool indicating whether the stream has enough data to be read. + bool CanReadData(uint32_t pos, uint32_t bytesRequested) + { + return audio_data_stream_can_read_data_from_position(m_haudioStream, bytesRequested, pos); + } + + /// + /// Reads a chunk of the audio data and fill it to given buffer + /// + /// A buffer to receive read data. + /// Size of the buffer. + /// Size of data filled to the buffer, 0 means end of stream + uint32_t ReadData(uint8_t* buffer, uint32_t bufferSize) + { + uint32_t filledSize = 0; + SPX_THROW_ON_FAIL(audio_data_stream_read(m_haudioStream, buffer, bufferSize, &filledSize)); + + return filledSize; + } + + /// + /// Reads a chunk of the audio data and fill it to given buffer, starting from the specified position. + /// + /// The position counting from start of the stream. + /// A buffer to receive read data. + /// Size of the buffer. + /// Size of data filled to the buffer, 0 means end of stream + uint32_t ReadData(uint32_t pos, uint8_t* buffer, uint32_t bufferSize) + { + uint32_t filledSize = 0; + SPX_THROW_ON_FAIL(audio_data_stream_read_from_position(m_haudioStream, buffer, bufferSize, pos, &filledSize)); + + return filledSize; + } + + /// + /// Save the audio data to a file, synchronously. + /// + /// The file name with full path. + void SaveToWavFile(const SPXSTRING& fileName) + { + SPX_THROW_ON_FAIL(audio_data_stream_save_to_wave_file(m_haudioStream, Utils::ToUTF8(fileName).c_str())); + } + + /// + /// Save the audio data to a file, asynchronously. + /// + /// The file name with full path. + /// An asynchronous operation representing the saving. + std::future SaveToWavFileAsync(const SPXSTRING& fileName) + { + auto keepAlive = this->shared_from_this(); + + auto future = std::async(std::launch::async, [keepAlive, this, fileName]() -> void { + SPX_THROW_ON_FAIL(audio_data_stream_save_to_wave_file(m_haudioStream, Utils::ToUTF8(fileName).c_str())); + }); + + return future; + } + + /// + /// Get current position of the audio data stream. + /// + /// Current position + uint32_t GetPosition() + { + uint32_t position = 0; + SPX_THROW_ON_FAIL(audio_data_stream_get_position(m_haudioStream, &position)); + return position; + } + + /// + /// Set current position of the audio data stream. + /// + /// Position to be set. + void SetPosition(uint32_t pos) + { + SPX_THROW_ON_FAIL(audio_data_stream_set_position(m_haudioStream, pos)); + } + + /// + /// Stops any more data from getting to the stream. + /// + void DetachInput() + { + SPX_THROW_ON_FAIL(audio_data_stream_detach_input(m_haudioStream)); + } + + /// + /// Explicit conversion operator. + /// + /// A handle. + explicit operator SPXAUDIOSTREAMHANDLE() { return m_haudioStream; } + + /// + /// Collection of additional SpeechSynthesisResult properties. + /// + const PropertyCollection& Properties; + +private: + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + explicit AudioDataStream(SPXAUDIOSTREAMHANDLE haudioStream) : + m_haudioStream(haudioStream), + m_properties(haudioStream), + Properties(m_properties) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + } +}; + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_processing_options.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_processing_options.h new file mode 100644 index 0000000..de3354b --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_processing_options.h @@ -0,0 +1,358 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_audio_processing_options.h: Public API declarations for AudioProcessingOptions and related C++ classes +// + +#pragma once +#include +#include +#include +#include +#include +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Audio { + +/// +/// Types of preset microphone array geometries. +/// See [Microphone Array Recommendations](/azure/cognitive-services/speech-service/speech-devices-sdk-microphone) for more details. +/// +enum class PresetMicrophoneArrayGeometry +{ + /// + /// Indicates that no geometry specified. Speech SDK will determine the microphone array geometry. + /// + Uninitialized, + /// + /// Indicates a microphone array with one microphone in the center and six microphones evenly spaced + /// in a circle with radius approximately equal to 42.5 mm. + /// + Circular7, + /// + /// Indicates a microphone array with one microphone in the center and three microphones evenly spaced + /// in a circle with radius approximately equal to 42.5 mm. + /// + Circular4, + /// + /// Indicates a microphone array with four linearly placed microphones with 40 mm spacing between them. + /// + Linear4, + /// + /// Indicates a microphone array with two linearly placed microphones with 40 mm spacing between them. + /// + Linear2, + /// + /// Indicates a microphone array with a single microphone. + /// + Mono, + /// + /// Indicates a microphone array with custom geometry. + /// + Custom +}; + +/// +/// Types of microphone arrays. +/// +enum class MicrophoneArrayType +{ + /// + /// Indicates that the microphone array has microphones in a straight line. + /// + Linear, + /// + /// Indicates that the microphone array has microphones in a plane. + /// + Planar +}; + +/// +/// Defines speaker reference channel position in input audio. +/// +enum class SpeakerReferenceChannel +{ + /// + /// Indicates that the input audio does not have a speaker reference channel. + /// + None, + /// + /// Indicates that the last channel in the input audio corresponds to the speaker + /// reference for echo cancellation. + /// + LastChannel +}; + +typedef AudioProcessingOptions_MicrophoneCoordinates MicrophoneCoordinates; + +/// +/// Represents the geometry of a microphone array. +/// +struct MicrophoneArrayGeometry +{ + /// + /// Type of microphone array. + /// + MicrophoneArrayType microphoneArrayType; + /// + /// Start angle for beamforming in degrees. + /// + uint16_t beamformingStartAngle; + /// + /// End angle for beamforming in degrees. + /// + uint16_t beamformingEndAngle; + /// + /// Coordinates of microphones in the microphone array. + /// + std::vector microphoneCoordinates; + + /// + /// Creates a new instance of MicrophoneArrayGeometry. + /// Beamforming start angle is set to zero. + /// Beamforming end angle is set to 180 degrees if microphoneArrayType is Linear, otherwise it is set to 360 degrees. + /// + /// Type of microphone array. + /// Coordinates of microphones in the microphone array. + MicrophoneArrayGeometry(MicrophoneArrayType microphoneArrayType, const std::vector& microphoneCoordinates) + { + this->microphoneArrayType = microphoneArrayType; + this->beamformingStartAngle = 0; + this->beamformingEndAngle = (microphoneArrayType == MicrophoneArrayType::Linear) ? 180 : 360; + this->microphoneCoordinates.resize(microphoneCoordinates.size()); + for (size_t i = 0; i < microphoneCoordinates.size(); i++) + { + this->microphoneCoordinates[i] = microphoneCoordinates[i]; + } + } + + /// + /// Creates a new instance of MicrophoneArrayGeometry. + /// + /// Type of microphone array. + /// Start angle for beamforming in degrees. + /// End angle for beamforming in degrees. + /// Coordinates of microphones in the microphone array. + MicrophoneArrayGeometry(MicrophoneArrayType microphoneArrayType, uint16_t beamformingStartAngle, uint16_t beamformingEndAngle, const std::vector& microphoneCoordinates) + { + this->microphoneArrayType = microphoneArrayType; + this->beamformingStartAngle = beamformingStartAngle; + this->beamformingEndAngle = beamformingEndAngle; + this->microphoneCoordinates.resize(microphoneCoordinates.size()); + for (size_t i = 0; i < microphoneCoordinates.size(); i++) + { + this->microphoneCoordinates[i] = microphoneCoordinates[i]; + } + } +}; + +/// +/// Represents audio processing options used with audio config class. +/// +class AudioProcessingOptions +{ +public: + + /// + /// Creates a new instance using the provided handle. + /// + /// A handle to audio processing options. + explicit AudioProcessingOptions(SPXAUDIOPROCESSINGOPTIONSHANDLE hoptions) + : m_hoptions(hoptions) + { + SPX_THROW_ON_FAIL(audio_processing_options_get_property_bag(m_hoptions, &m_propertybag)); + } + + /// + /// Destructs an instance of the AudioProcessingOptions class. + /// + ~AudioProcessingOptions() = default; + + /// + /// Internal operator used to get underlying handle value. + /// + /// A handle. + explicit operator SPXAUDIOPROCESSINGOPTIONSHANDLE() const { return m_hoptions.get(); } + + /// + /// Creates a new instance of the AudioProcessingOptions class. + /// + /// Specifies flags to control the audio processing performed by Speech SDK. It is bitwise OR of AUDIO_INPUT_PROCESSING_XXX constants. + /// The newly created AudioProcessingOptions wrapped inside a std::shared_ptr. + /// + /// This function should only be used when the audio input is from a microphone array. + /// On Windows, this function will try to query the microphone array geometry from the audio driver. Audio data is also read from speaker reference channel. + /// On Linux, it assumes that the microphone is a single channel microphone. + /// + static std::shared_ptr Create(int audioProcessingFlags) + { + SPXAUDIOPROCESSINGOPTIONSHANDLE hoptions = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_processing_options_create(&hoptions, audioProcessingFlags)); + + auto options = new AudioProcessingOptions(hoptions); + return std::shared_ptr(options); + } + + /// + /// Creates a new instance of the AudioProcessingOptions class with preset microphone array geometry. + /// + /// Specifies flags to control the audio processing performed by Speech SDK. It is bitwise OR of AUDIO_INPUT_PROCESSING_XXX constants. + /// Specifies the type of microphone array geometry. + /// Specifies the speaker reference channel position in the input audio. + /// The newly created AudioProcessingOptions wrapped inside a std::shared_ptr. + static std::shared_ptr Create(int audioProcessingFlags, PresetMicrophoneArrayGeometry microphoneArrayGeometry, SpeakerReferenceChannel speakerReferenceChannel = SpeakerReferenceChannel::None) + { + SPXAUDIOPROCESSINGOPTIONSHANDLE hoptions = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_processing_options_create_from_preset_microphone_array_geometry(&hoptions, audioProcessingFlags, (AudioProcessingOptions_PresetMicrophoneArrayGeometry)microphoneArrayGeometry, (AudioProcessingOptions_SpeakerReferenceChannel)speakerReferenceChannel)); + + auto options = new AudioProcessingOptions(hoptions); + return std::shared_ptr(options); + } + + /// + /// Creates a new instance of the AudioProcessingOptions class with microphone array geometry. + /// + /// Specifies flags to control the audio processing performed by Speech SDK. It is bitwise OR of AUDIO_INPUT_PROCESSING_XXX constants. + /// Specifies the microphone array geometry. + /// Specifies the speaker reference channel position in the input audio. + /// The newly created AudioProcessingOptions wrapped inside a std::shared_ptr. + static std::shared_ptr Create(int audioProcessingFlags, MicrophoneArrayGeometry microphoneArrayGeometry, SpeakerReferenceChannel speakerReferenceChannel = SpeakerReferenceChannel::None) + { + AudioProcessingOptions_MicrophoneArrayGeometry geometry + { + (AudioProcessingOptions_MicrophoneArrayType)microphoneArrayGeometry.microphoneArrayType, + microphoneArrayGeometry.beamformingStartAngle, + microphoneArrayGeometry.beamformingEndAngle, + (uint16_t)microphoneArrayGeometry.microphoneCoordinates.size(), + microphoneArrayGeometry.microphoneCoordinates.data() + }; + + SPXAUDIOPROCESSINGOPTIONSHANDLE hoptions = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_processing_options_create_from_microphone_array_geometry(&hoptions, audioProcessingFlags, &geometry, (AudioProcessingOptions_SpeakerReferenceChannel)speakerReferenceChannel)); + + auto options = new AudioProcessingOptions(hoptions); + return std::shared_ptr(options); + } + + /// + /// Returns the type of audio processing performed by Speech SDK. + /// + /// Bitwise OR of AUDIO_INPUT_PROCESSING_XXX constant flags indicating the input audio processing performed by Speech SDK. + int GetAudioProcessingFlags() const + { + int audioProcessingFlags; + SPX_THROW_ON_FAIL(audio_processing_options_get_audio_processing_flags(m_hoptions, &audioProcessingFlags)); + return audioProcessingFlags; + } + + /// + /// Returns the microphone array geometry of the microphone used for audio input. + /// + /// A value of type PresetMicrophoneArrayGeometry enum. + PresetMicrophoneArrayGeometry GetPresetMicrophoneArrayGeometry() const + { + PresetMicrophoneArrayGeometry microphoneArrayGeometry = PresetMicrophoneArrayGeometry::Uninitialized; + SPX_THROW_ON_FAIL(audio_processing_options_get_preset_microphone_array_geometry(m_hoptions, (AudioProcessingOptions_PresetMicrophoneArrayGeometry*)µphoneArrayGeometry)); + return microphoneArrayGeometry; + } + + /// + /// Returns the microphone array type of the microphone used for audio input. + /// + /// A value of type MicrophoneArrayType enum. + MicrophoneArrayType GetMicrophoneArrayType() const + { + MicrophoneArrayType microphoneArrayType = MicrophoneArrayType::Linear; + SPX_THROW_ON_FAIL(audio_processing_options_get_microphone_array_type(m_hoptions, (AudioProcessingOptions_MicrophoneArrayType*)µphoneArrayType)); + return microphoneArrayType; + } + + /// + /// Returns the start angle used for beamforming. + /// + /// Beamforming start angle. + uint16_t GetBeamformingStartAngle() const + { + uint16_t startAngle; + SPX_THROW_ON_FAIL(audio_processing_options_get_beamforming_start_angle(m_hoptions, &startAngle)); + return startAngle; + } + + /// + /// Returns the end angle used for beamforming. + /// + /// Beamforming end angle. + uint16_t GetBeamformingEndAngle() const + { + uint16_t endAngle; + SPX_THROW_ON_FAIL(audio_processing_options_get_beamforming_end_angle(m_hoptions, &endAngle)); + return endAngle; + } + + /// + /// Returns the coordinates of microphones in the microphone array used for audio input. + /// + /// A std::vector of MicrophoneCoordinates elements. + std::vector GetMicrophoneCoordinates() const + { + uint16_t microphoneCount; + SPX_THROW_ON_FAIL(audio_processing_options_get_microphone_count(m_hoptions, µphoneCount)); + + std::vector microphoneCoordinates(microphoneCount); + SPX_THROW_ON_FAIL(audio_processing_options_get_microphone_coordinates(m_hoptions, microphoneCoordinates.data(), microphoneCount)); + return microphoneCoordinates; + } + + /// + /// Returns the speaker reference channel position in the audio input. + /// + /// A value of type SpeakerReferenceChannel enum. + SpeakerReferenceChannel GetSpeakerReferenceChannel() const + { + SpeakerReferenceChannel speakerReferenceChannel = SpeakerReferenceChannel::None; + SPX_THROW_ON_FAIL(audio_processing_options_get_speaker_reference_channel(m_hoptions, (AudioProcessingOptions_SpeakerReferenceChannel*)&speakerReferenceChannel)); + return speakerReferenceChannel; + } + + /// + /// Sets a property value by name. + /// + /// The property name. + /// The property value. + void SetProperty(const SPXSTRING& name, const SPXSTRING& value) + { + property_bag_set_string(m_propertybag, -1, Utils::ToUTF8(name).c_str(), Utils::ToUTF8(value).c_str()); + } + + /// + /// Gets a property value by name. + /// + /// The parameter name. + /// The property value. + SPXSTRING GetProperty(const SPXSTRING& name) const + { + const char* value = property_bag_get_string(m_propertybag, -1, Utils::ToUTF8(name).c_str(), ""); + return Utils::ToSPXString(Utils::CopyAndFreePropertyString(value)); + } + +private: + + DISABLE_COPY_AND_MOVE(AudioProcessingOptions); + + /// + /// Internal member variable that holds the smart handle. + /// + SmartHandle m_hoptions; + + /// + /// Internal member variable that holds the properties of the audio processing options. + /// + SmartHandle m_propertybag; +}; + +} } } } // Microsoft::CognitiveServices::Speech::Audio diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_stream.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_stream.h new file mode 100644 index 0000000..6a3f7d2 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_stream.h @@ -0,0 +1,995 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_audio_stream.h: Public API declarations for AudioInputStream / AudioOutputStream and related C++ classes +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +namespace Dialog { + class ActivityReceivedEventArgs; +} + +namespace Audio { + + + +class PullAudioInputStreamCallback; +class PushAudioInputStream; +class PullAudioInputStream; +class PushAudioOutputStreamCallback; +class PushAudioOutputStream; +class PullAudioOutputStream; + + +/// +/// Represents audio input stream used for custom audio input configurations. +/// +class AudioInputStream +{ +public: + + using ReadCallbackFunction_Type = ::std::function; + using CloseCallbackFunction_Type = ::std::function; + /// Added in version 1.5.0. + using GetPropertyCallbackFunction_Type = std::function; + + /// + /// Internal operator used to get underlying handle value. + /// + /// A handle. + explicit operator SPXAUDIOSTREAMHANDLE() const { return m_haudioStream.get(); } + + /// + /// Creates a memory backed PushAudioInputStream using the default format (16 kHz, 16 bit, mono PCM). + /// + /// A shared pointer to PushAudioInputStream + static std::shared_ptr CreatePushStream(); + + /// + /// Creates a memory backed PushAudioInputStream with the specified audio format. + /// + /// Audio stream format. + /// A shared pointer to PushAudioInputStream + static std::shared_ptr CreatePushStream(std::shared_ptr format); + + /// + /// Creates a PullAudioInputStream that delegates to the specified callback functions for Read() and Close() methods, using the default format (16 kHz, 16 bit, mono PCM). + /// + /// Context pointer to use when invoking the callbacks. + /// Read callback. + /// Close callback. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr CreatePullStream(void* pvContext, CUSTOM_AUDIO_PULL_STREAM_READ_CALLBACK readCallback, CUSTOM_AUDIO_PULL_STREAM_CLOSE_CALLBACK closeCallback = nullptr); + + /// + /// Creates a PullAudioInputStream that delegates to the specified callback functions for Read(), Close() and GetProperty() methods + /// Added in version 1.5.0. + /// + /// Context pointer to use when invoking the callbacks. + /// Read callback. + /// Close callback. + /// GetProperty callback. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr CreatePullStream(void* pvContext, CUSTOM_AUDIO_PULL_STREAM_READ_CALLBACK readCallback, CUSTOM_AUDIO_PULL_STREAM_CLOSE_CALLBACK closeCallback, CUSTOM_AUDIO_PULL_STREAM_GET_PROPERTY_CALLBACK getPropertyCallback); + + /// Creates a PullAudioInputStream that delegates to the specified callback functions for Read() and Close() methods, using the default format (16 kHz, 16 bit, mono PCM). + /// + /// Read callback. + /// Close callback. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr CreatePullStream(ReadCallbackFunction_Type readCallback, CloseCallbackFunction_Type closeCallback = nullptr); + + /// + /// Creates a PullAudioInputStream that delegates to the specified callback functions for Read(), Close() and GetProperty() methods. + /// Added in version 1.5.0. + /// + /// Read callback. + /// Close callback. + /// Get property callback. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr CreatePullStream(ReadCallbackFunction_Type readCallback, CloseCallbackFunction_Type closeCallback, GetPropertyCallbackFunction_Type getPropertyCallback); + + + /// + /// Creates a PullAudioInputStream that delegates to the specified callback interface for the Read() and Close() methods, using the default format (16 kHz, 16 bit, mono PCM). + /// + /// Shared pointer to PullAudioInputStreamCallback instance. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr CreatePullStream(std::shared_ptr callback); + + /// + /// Creates a PullAudioInputStream that delegates to the specified callback functions for Read() and Close() methods. + /// + /// Audio stream format. + /// Context pointer to use when invoking the callbacks. + /// Read callback. + /// Close callback. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr CreatePullStream(std::shared_ptr format, void* pvContext, CUSTOM_AUDIO_PULL_STREAM_READ_CALLBACK readCallback, CUSTOM_AUDIO_PULL_STREAM_CLOSE_CALLBACK closeCallback = nullptr); + + /// + /// Creates a PullAudioInputStream that delegates to the specified callback functions for Read(), Close() and GetProperty() methods. + /// Added in version 1.5.0. + /// + /// Audio stream format. + /// Context pointer to use when invoking the callbacks. + /// Read callback. + /// Close callback. + /// Get property callback. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr CreatePullStream(std::shared_ptr format, void* pvContext, CUSTOM_AUDIO_PULL_STREAM_READ_CALLBACK readCallback, CUSTOM_AUDIO_PULL_STREAM_CLOSE_CALLBACK closeCallback, CUSTOM_AUDIO_PULL_STREAM_GET_PROPERTY_CALLBACK getPropertyCallback); + + /// Creates a PullAudioInputStream that delegates to the specified callback functions for Read() and Close() methods. + /// + /// Audio stream format. + /// Read callback. + /// Close callback. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr CreatePullStream(std::shared_ptr format, ReadCallbackFunction_Type readCallback, CloseCallbackFunction_Type closeCallback = nullptr); + + /// + /// Creates a PullAudioInputStream that delegates to the specified callback functions for Read() and Close() methods. + /// Added in version 1.5.0. + /// + /// Audio stream format. + /// Read callback. + /// Close callback. + /// Get property callback. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr CreatePullStream(std::shared_ptr format, ReadCallbackFunction_Type readCallback, CloseCallbackFunction_Type closeCallback, GetPropertyCallbackFunction_Type getPropertyCallback); + + + /// + /// Creates a PullAudioInputStream that delegates to the specified callback interface for the Read() and Close() methods, using the specified format. + /// + /// Audio stream format. + /// Shared pointer to PullAudioInputStreamCallback instance. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr CreatePullStream(std::shared_ptr format, std::shared_ptr callback); + +protected: + + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + explicit AudioInputStream(SPXAUDIOSTREAMHANDLE haudioStream) : m_haudioStream(haudioStream) { } + + /// + /// Destructor, does nothing. + /// + virtual ~AudioInputStream() {} + + /// + /// Internal helper method to get the default format if the specified format is nullptr. + /// + static std::shared_ptr UseDefaultFormatIfNull(std::shared_ptr format) { return format != nullptr ? format : AudioStreamFormat::GetDefaultInputFormat(); } + + /// + /// Internal helper method to get the audio stream format handle. + /// + static SPXAUDIOSTREAMFORMATHANDLE GetFormatHandle(std::shared_ptr format) { return (SPXAUDIOSTREAMFORMATHANDLE)(*format.get()); } + + /// + /// Internal member variable that holds the smart handle. + /// + SmartHandle m_haudioStream; + + protected: + static constexpr size_t m_maxPropertyLen = 1024; + + /*! \endcond */ + +private: + + DISABLE_COPY_AND_MOVE(AudioInputStream); +}; + + +/// +/// Represents memory backed push audio input stream used for custom audio input configurations. +/// +class PushAudioInputStream : public AudioInputStream +{ +public: + + /// + /// Destructor; closes the underlying stream if not already closed. + /// + virtual ~PushAudioInputStream() + { + if (audio_stream_is_handle_valid(m_haudioStream)) + { + CloseStream(); + } + } + + /// + /// Creates a memory backed PushAudioInputStream using the default format (16 kHz, 16 bit, mono PCM). + /// + /// A shared pointer to PushAudioInputStream + static std::shared_ptr Create() + { + return Create(nullptr); + } + + /// + /// Creates a memory backed PushAudioInputStream with the specified audio format. + /// + /// Audio stream format. + /// A shared pointer to PushAudioInputStream + static std::shared_ptr Create(std::shared_ptr format) + { + format = UseDefaultFormatIfNull(format); + + SPXAUDIOSTREAMHANDLE haudioStream = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_stream_create_push_audio_input_stream(&haudioStream, GetFormatHandle(format))); + + auto stream = new PushAudioInputStream(haudioStream); + return std::shared_ptr(stream); + } + + /// + /// Writes the audio data specified by making an internal copy of the data. + /// Note: The dataBuffer should not contain any audio header. + /// + /// The pointer to the audio buffer of which this function will make a copy. + /// The size of the buffer. + void Write(uint8_t* dataBuffer, uint32_t size) + { + SPX_THROW_ON_FAIL(push_audio_input_stream_write(m_haudioStream, dataBuffer, size)); + } + + /// + /// Set value of a property. The properties of the audio data should be set before writing the audio data. + /// Added in version 1.5.0. + /// + /// The id of property. See + /// value to set + void SetProperty(PropertyId id, const SPXSTRING& value) + { + SPX_THROW_ON_FAIL(push_audio_input_stream_set_property_by_id(m_haudioStream, static_cast(id), Utils::ToUTF8(value).c_str())); + } + + /// + /// Set value of a property. The properties of the audio data should be set before writing the audio data. + /// Added in version 1.5.0. + /// + /// The name of property. + /// value to set + void SetProperty(const SPXSTRING& propertyName, const SPXSTRING& value) + { + SPX_THROW_ON_FAIL(push_audio_input_stream_set_property_by_name(m_haudioStream, Utils::ToUTF8(propertyName.c_str()), Utils::ToUTF8(value.c_str()))); + } + + /// + /// Closes the stream. + /// + void Close() { SPX_THROW_ON_FAIL(CloseStream()); } + + +protected: + + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + explicit PushAudioInputStream(SPXAUDIOSTREAMHANDLE haudioStream) : AudioInputStream(haudioStream) { } + + /*! \endcond */ + +private: + + DISABLE_COPY_AND_MOVE(PushAudioInputStream); + + SPXHR CloseStream() { return push_audio_input_stream_close(m_haudioStream); } +}; + + +/// +/// An interface that defines callback methods for an audio input stream. +/// +/// +/// Derive from this class and implement its function to provide your own +/// data as an audio input stream. +/// +class PullAudioInputStreamCallback +{ +public: + + /// + /// Destructor, does nothing. + /// + virtual ~PullAudioInputStreamCallback() {} + + /// + /// This function is called to synchronously get data from the audio stream. + /// Note: The dataBuffer returned by Read() should not contain any audio header. + /// + /// The pointer to the buffer to which to copy the audio data. + /// The size of the buffer. + /// The number of bytes copied into the buffer, or zero to indicate end of stream + virtual int Read(uint8_t* dataBuffer, uint32_t size) = 0; + + /// + /// This function is called to synchronously to get meta information associated to stream data, such as TimeStamp or UserId . + /// Added in version 1.5.0. + /// + /// The id of the property. + /// The value of the property. + virtual SPXSTRING GetProperty(PropertyId id) + { + if (PropertyId::DataBuffer_TimeStamp == id) + { + return ""; + } + else if (PropertyId::DataBuffer_UserId == id) + { + return ""; + } + else + { + return ""; + } + } + + /// + /// This function is called to close the audio stream. + /// + /// + virtual void Close() = 0; + +protected: + + /*! \cond PROTECTED */ + + /// + /// Constructor, does nothing. + /// + PullAudioInputStreamCallback() {}; + + /*! \endcond */ + +private: + + DISABLE_COPY_AND_MOVE(PullAudioInputStreamCallback); +}; + + +/// +/// Pull audio input stream class. +/// +class PullAudioInputStream : public AudioInputStream +{ +public: + + /// + /// Creates a PullAudioInputStream utilizing the specified Read() and Close() "C" callback functions pointers + /// Note: The dataBuffer returned by Read() should not contain any audio header. + /// + /// Context pointer to use when invoking the callbacks. + /// Read callback. + /// Close callback. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr Create(void* pvContext, CUSTOM_AUDIO_PULL_STREAM_READ_CALLBACK readCallback, CUSTOM_AUDIO_PULL_STREAM_CLOSE_CALLBACK closeCallback = nullptr) + { + return Create(nullptr, pvContext, readCallback, closeCallback); + } + + /// + /// Creates a PullAudioInputStream utilizing the specified Read(), Close() and GetProperty() "C" callback functions pointers + /// Note: The dataBuffer returned by Read() should not contain any audio header. + /// Added in version 1.5.0. + /// + /// Context pointer to use when invoking the callbacks. + /// Read callback. + /// Close callback. + /// Get property callback. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr Create(void* pvContext, CUSTOM_AUDIO_PULL_STREAM_READ_CALLBACK readCallback, CUSTOM_AUDIO_PULL_STREAM_CLOSE_CALLBACK closeCallback, CUSTOM_AUDIO_PULL_STREAM_GET_PROPERTY_CALLBACK getPropertyCallback) + { + return Create(nullptr, pvContext, readCallback, closeCallback, getPropertyCallback); + } + + /// Creates a PullAudioInputStream utilizing the specified Read() and Close() callback functions. + /// Note: The dataBuffer returned by Read() should not contain any audio header. + /// + /// Read callback. + /// Close callback. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr Create(ReadCallbackFunction_Type readCallback, CloseCallbackFunction_Type closeCallback = nullptr) + { + return Create(nullptr, readCallback, closeCallback); + } + + /// + /// Creates a PullAudioInputStream utilizing the specified Read(), Close() and GetProperty() callback functions. + /// Note: The dataBuffer returned by Read() should not contain any audio header. + /// Added in version 1.5.0. + /// + /// Read callback. + /// Close callback. + /// Get property callback. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr Create(ReadCallbackFunction_Type readCallback, CloseCallbackFunction_Type closeCallback, GetPropertyCallbackFunction_Type getPropertyCallback) + { + return Create(nullptr, readCallback, closeCallback, getPropertyCallback); + } + + /// + /// Creates a PullAudioInputStream utilizing the specified Close() callback function. + /// + /// Shared pointer to PullAudioInputStreamCallback instance. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr Create(std::shared_ptr callback) + { + return Create(nullptr, callback); + } + + /// + /// Creates a PullAudioInputStream utilizing the specified Read() and Close() "C" callback functions pointers + /// Note: The dataBuffer returned by Read() should not contain any audio header. + /// + /// Audio stream format. + /// Context pointer to use when invoking the callbacks. + /// Read callback. + /// Close callback. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr Create(std::shared_ptr format, void* pvContext, CUSTOM_AUDIO_PULL_STREAM_READ_CALLBACK readCallback, CUSTOM_AUDIO_PULL_STREAM_CLOSE_CALLBACK closeCallback = nullptr) + { + return Create(format, + [=](uint8_t* buffer, uint32_t size) -> int { return readCallback(pvContext, buffer, size); }, + [=]() { if (closeCallback != nullptr) { closeCallback(pvContext); } }); + } + + /// + /// Creates a PullAudioInputStream utilizing the specified Read(), Close() and GetProperty() "C" callback functions pointers + /// Note: The dataBuffer returned by Read() should not contain any audio header. + /// + /// Audio stream format. + /// Context pointer to use when invoking the callbacks. + /// Read callback. + /// Close callback. + /// Get property callback. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr Create(std::shared_ptr format, void* pvContext, CUSTOM_AUDIO_PULL_STREAM_READ_CALLBACK readCallback, CUSTOM_AUDIO_PULL_STREAM_CLOSE_CALLBACK closeCallback, CUSTOM_AUDIO_PULL_STREAM_GET_PROPERTY_CALLBACK getPropertyCallback) + { + return Create(format, + [=](uint8_t* buffer, uint32_t size) -> int { return readCallback(pvContext, buffer, size); }, + [=]() { if (closeCallback != nullptr) { closeCallback(pvContext); } }, + [=](PropertyId id) -> SPXSTRING + { + uint8_t result[m_maxPropertyLen]; + getPropertyCallback(pvContext, static_cast(id), result, m_maxPropertyLen); + return reinterpret_cast(result); + }); + } + + /// + /// Creates a PullAudioInputStream utilizing the specified Read() and Close() callback functions. + /// Note: The dataBuffer returned by Read() should not contain any audio header. + /// + /// Audio stream format. + /// Read callback. + /// Close callback. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr Create(std::shared_ptr format, ReadCallbackFunction_Type readCallback, CloseCallbackFunction_Type closeCallback = nullptr) + { + auto wrapper = std::make_shared(readCallback, closeCallback); + return Create(format, wrapper); + } + + /// + /// Creates a PullAudioInputStream utilizing the specified Read(), Close() and GetProperty() callback functions. + /// Note: The dataBuffer returned by Read() should not contain any audio header. + /// Added in version 1.5.0. + /// + /// Audio stream format. + /// Read callback. + /// Close callback. + /// Get property callback. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr Create(std::shared_ptr format, ReadCallbackFunction_Type readCallback, CloseCallbackFunction_Type closeCallback, GetPropertyCallbackFunction_Type getPropertyCallback) + { + auto wrapper = std::make_shared(readCallback, closeCallback, getPropertyCallback); + return Create(format, wrapper); + } + + /// + /// Creates a PullAudioInputStream utilizing the specified Read() and Close() callback functions. + /// + /// Audio stream format. + /// Shared pointer to PullAudioInputStreamCallback instance. + /// A shared pointer to PullAudioInputStream + static std::shared_ptr Create(std::shared_ptr format, std::shared_ptr callback) + { + format = UseDefaultFormatIfNull(format); + + SPXAUDIOSTREAMHANDLE haudioStream = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_stream_create_pull_audio_input_stream(&haudioStream, GetFormatHandle(format))); + + auto stream = new PullAudioInputStream(haudioStream); + SPX_THROW_ON_FAIL(pull_audio_input_stream_set_callbacks(haudioStream, stream, ReadCallbackWrapper, CloseCallbackWrapper)); + SPX_THROW_ON_FAIL(pull_audio_input_stream_set_getproperty_callback(haudioStream, stream, GetPropertyCallbackWrapper)); + + stream->m_callback = callback; + + return std::shared_ptr(stream); + } + +protected: + + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + explicit PullAudioInputStream(SPXAUDIOSTREAMHANDLE haudioStream) : AudioInputStream(haudioStream) { } + + class FunctionCallbackWrapper : public PullAudioInputStreamCallback + { + public: + + FunctionCallbackWrapper(ReadCallbackFunction_Type readCallback, CloseCallbackFunction_Type closeCallback, GetPropertyCallbackFunction_Type getPropertyCallback = nullptr) : + m_readCallback(readCallback), + m_closeCallback(closeCallback), + m_getPropertyCallback(getPropertyCallback) + { + }; + + /// Note: The dataBuffer returned by Read() should not contain any audio header. + int Read(uint8_t* dataBuffer, uint32_t size) override { return m_readCallback(dataBuffer, size); } + void Close() override { if (m_closeCallback != nullptr) m_closeCallback(); }; + SPXSTRING GetProperty(PropertyId id) override + { + if (m_getPropertyCallback != nullptr) + { + return m_getPropertyCallback(id); + } + else + { + return ""; + } + } + + private: + + DISABLE_COPY_AND_MOVE(FunctionCallbackWrapper); + + ReadCallbackFunction_Type m_readCallback; + CloseCallbackFunction_Type m_closeCallback; + GetPropertyCallbackFunction_Type m_getPropertyCallback; + + }; + + /*! \endcond */ + +private: + + DISABLE_COPY_AND_MOVE(PullAudioInputStream); + + static int ReadCallbackWrapper(void* pvContext, uint8_t* dataBuffer, uint32_t size) + { + PullAudioInputStream* ptr = (PullAudioInputStream*)pvContext; + return ptr->m_callback->Read(dataBuffer, size); + } + + static void CloseCallbackWrapper(void* pvContext) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + PullAudioInputStream* ptr = (PullAudioInputStream*)pvContext; + ptr->m_callback->Close(); + } + + static void GetPropertyCallbackWrapper(void *pvContext, int id, uint8_t* result, uint32_t size) + { + PullAudioInputStream* ptr = (PullAudioInputStream*)pvContext; + auto value = ptr->m_callback->GetProperty(static_cast(id)); + auto valueSize = value.size() + 1; + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, valueSize > size); + std::memcpy(result, value.c_str(), valueSize); + } + + std::shared_ptr m_callback; +}; + + +inline std::shared_ptr AudioInputStream::CreatePushStream() +{ + return PushAudioInputStream::Create(); +} + +inline std::shared_ptr AudioInputStream::CreatePushStream(std::shared_ptr format) +{ + return PushAudioInputStream::Create(format); +} + +inline std::shared_ptr AudioInputStream::CreatePullStream(void* pvContext, CUSTOM_AUDIO_PULL_STREAM_READ_CALLBACK readCallback, CUSTOM_AUDIO_PULL_STREAM_CLOSE_CALLBACK closeCallback) +{ + return PullAudioInputStream::Create(pvContext, readCallback, closeCallback); +} + +inline std::shared_ptr AudioInputStream::CreatePullStream(void* pvContext, CUSTOM_AUDIO_PULL_STREAM_READ_CALLBACK readCallback, CUSTOM_AUDIO_PULL_STREAM_CLOSE_CALLBACK closeCallback, CUSTOM_AUDIO_PULL_STREAM_GET_PROPERTY_CALLBACK getPropertyCallback) +{ + return PullAudioInputStream::Create(pvContext, readCallback, closeCallback, getPropertyCallback); +} + +inline std::shared_ptr AudioInputStream::CreatePullStream(ReadCallbackFunction_Type readCallback, CloseCallbackFunction_Type closeCallback) +{ + return PullAudioInputStream::Create(readCallback, closeCallback); +} + +inline std::shared_ptr AudioInputStream::CreatePullStream(ReadCallbackFunction_Type readCallback, CloseCallbackFunction_Type closeCallback, GetPropertyCallbackFunction_Type getPropertyCallback) +{ + return PullAudioInputStream::Create(readCallback, closeCallback, getPropertyCallback); +} + +inline std::shared_ptr AudioInputStream::CreatePullStream(std::shared_ptr callback) +{ + return PullAudioInputStream::Create(callback); +} + +inline std::shared_ptr AudioInputStream::CreatePullStream(std::shared_ptr format, void* pvContext, CUSTOM_AUDIO_PULL_STREAM_READ_CALLBACK readCallback, CUSTOM_AUDIO_PULL_STREAM_CLOSE_CALLBACK closeCallback) +{ + return PullAudioInputStream::Create(format, pvContext, readCallback, closeCallback); +} + +inline std::shared_ptr AudioInputStream::CreatePullStream(std::shared_ptr format, void* pvContext, CUSTOM_AUDIO_PULL_STREAM_READ_CALLBACK readCallback, CUSTOM_AUDIO_PULL_STREAM_CLOSE_CALLBACK closeCallback, CUSTOM_AUDIO_PULL_STREAM_GET_PROPERTY_CALLBACK getPropertyCallback) +{ + return PullAudioInputStream::Create(format, pvContext, readCallback, closeCallback, getPropertyCallback); +} + +inline std::shared_ptr AudioInputStream::CreatePullStream(std::shared_ptr format, ReadCallbackFunction_Type readCallback, CloseCallbackFunction_Type closeCallback) +{ + return PullAudioInputStream::Create(format, readCallback, closeCallback); +} + +inline std::shared_ptr AudioInputStream::CreatePullStream(std::shared_ptr format, ReadCallbackFunction_Type readCallback, CloseCallbackFunction_Type closeCallback, GetPropertyCallbackFunction_Type getPropertyCallback) +{ + return PullAudioInputStream::Create(format, readCallback, closeCallback, getPropertyCallback); +} + +inline std::shared_ptr AudioInputStream::CreatePullStream(std::shared_ptr format, std::shared_ptr callback) +{ + return PullAudioInputStream::Create(format, callback); +} + + +/// +/// Represents audio output stream used for custom audio output configurations. +/// Updated in version 1.7.0 +/// +class AudioOutputStream +{ +public: + + using WriteCallbackFunction_Type = ::std::function; + using CloseCallbackFunction_Type = ::std::function; + + /// + /// Internal operator used to get underlying handle value. + /// + /// A handle. + explicit operator SPXAUDIOSTREAMHANDLE() const { return m_haudioStream.get(); } + + /// + /// Creates a memory backed PullAudioOutputStream. + /// + /// A shared pointer to PullAudioOutputStream + static std::shared_ptr CreatePullStream(); + + /// + /// Creates a PushAudioOutputStream that delegates to the specified callback functions for Write() and Close() methods. + /// + /// Context pointer to use when invoking the callbacks. + /// Write callback. + /// Close callback. + /// A shared pointer to PushAudioOutputStream + static std::shared_ptr CreatePushStream(void* pvContext, CUSTOM_AUDIO_PUSH_STREAM_WRITE_CALLBACK writeCallback, CUSTOM_AUDIO_PUSH_STREAM_CLOSE_CALLBACK closeCallback = nullptr); + + /// + /// Creates a PushAudioOutputStream that delegates to the specified callback functions for Write() and Close() methods. + /// + /// Write callback. + /// Close callback. + /// A shared pointer to PushAudioOutputStream + static std::shared_ptr CreatePushStream(WriteCallbackFunction_Type writeCallback, CloseCallbackFunction_Type closeCallback = nullptr); + + /// + /// Creates a PushAudioOutputStream that delegates to the specified callback interface for Write() and Close() methods. + /// + /// Shared pointer to PushAudioOutputStreamCallback instance. + /// A shared pointer to PushAudioOutputStream + static std::shared_ptr CreatePushStream(std::shared_ptr callback); + +protected: + + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + explicit AudioOutputStream(SPXAUDIOSTREAMHANDLE haudioStream) : m_haudioStream(haudioStream) { } + + /// + /// Destructor, does nothing. + /// + virtual ~AudioOutputStream() {} + + /// + /// Internal member variable that holds the smart handle. + /// + SmartHandle m_haudioStream; + + /*! \endcond */ + +private: + + DISABLE_COPY_AND_MOVE(AudioOutputStream); +}; + + +/// +/// Represents memory backed pull audio output stream used for custom audio output. +/// Updated in version 1.7.0 +/// +class PullAudioOutputStream : public AudioOutputStream +{ +public: + friend class Dialog::ActivityReceivedEventArgs; + + /// + /// Creates a memory backed PullAudioOutputStream. + /// + /// A shared pointer to PullAudioOutputStream + static std::shared_ptr Create() + { + SPXAUDIOSTREAMHANDLE haudioStream = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_stream_create_pull_audio_output_stream(&haudioStream)); + + auto stream = new PullAudioOutputStream(haudioStream); + return std::shared_ptr(stream); + } + + /// + /// Reads a chunk of the audio data and fill it to given buffer + /// + /// A buffer to receive read data. + /// Size of the buffer. + /// Size of data filled to the buffer, 0 means end of stream + inline uint32_t Read(uint8_t* buffer, uint32_t bufferSize) + { + uint32_t filledSize = 0; + SPX_THROW_ON_FAIL(pull_audio_output_stream_read(m_haudioStream, buffer, bufferSize, &filledSize)); + + return filledSize; + } + + +protected: + + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + explicit PullAudioOutputStream(SPXAUDIOSTREAMHANDLE haudioStream) : AudioOutputStream(haudioStream) { } + + /*! \endcond */ + + +private: + + template + static std::shared_ptr SpxAllocSharedBuffer(size_t sizeInBytes) + { + auto ptr = reinterpret_cast(new uint8_t[sizeInBytes]); + auto deleter = [](T* p) { delete[] reinterpret_cast(p); }; + + std::shared_ptr buffer(ptr, deleter); + return buffer; + } + + +private: + + DISABLE_COPY_AND_MOVE(PullAudioOutputStream); + + std::vector m_audioData; +}; + + +/// +/// An interface that defines callback methods for an audio output stream. +/// Updated in version 1.7.0 +/// +/// +/// Derive from this class and implement its function to provide your own +/// data as an audio output stream. +/// +class PushAudioOutputStreamCallback +{ +public: + + /// + /// Destructor, does nothing. + /// + virtual ~PushAudioOutputStreamCallback() {} + + /// + /// This function is called to synchronously put data to the audio stream. + /// + /// The pointer to the buffer from which to consume the audio data. + /// The size of the buffer. + /// The number of bytes consumed from the buffer + virtual int Write(uint8_t* dataBuffer, uint32_t size) = 0; + + /// + /// This function is called to close the audio stream. + /// + /// + virtual void Close() = 0; + +protected: + + /*! \cond PROTECTED */ + + /// + /// Constructor, does nothing. + /// + PushAudioOutputStreamCallback() {}; + + /*! \endcond */ + +private: + + DISABLE_COPY_AND_MOVE(PushAudioOutputStreamCallback); +}; + + +/// +/// Push audio output stream class. +/// Added in version 1.4.0 +/// +class PushAudioOutputStream : public AudioOutputStream +{ +public: + + /// + /// Creates a PushAudioOutputStream utilizing the specified Write() and Close() "C" callback functions pointers + /// + /// Context pointer to use when invoking the callbacks. + /// Write callback. + /// Close callback. + /// A shared pointer to PushAudioOutputStream + static std::shared_ptr Create(void* pvContext, CUSTOM_AUDIO_PUSH_STREAM_WRITE_CALLBACK writeCallback, CUSTOM_AUDIO_PUSH_STREAM_CLOSE_CALLBACK closeCallback = nullptr) + { + return Create( + [=](uint8_t* buffer, uint32_t size) -> int { return writeCallback(pvContext, buffer, size); }, + [=]() { if (closeCallback != nullptr) { closeCallback(pvContext); } }); + } + + /// + /// Creates a PushAudioOutputStream utilizing the specified Write() and Close() callback functions. + /// + /// Write callback. + /// Close callback. + /// A shared pointer to PushAudioOutputStream + static std::shared_ptr Create(WriteCallbackFunction_Type writeCallback, CloseCallbackFunction_Type closeCallback = nullptr) + { + auto wrapper = std::make_shared(writeCallback, closeCallback); + return Create(wrapper); + } + + /// + /// Creates a PushAudioOutputStream utilizing the specified callback interface with Write() and Close() callback function. + /// + /// Shared pointer to PushAudioOutputStreamCallback instance. + /// A shared pointer to PushAudioOutputStream + static std::shared_ptr Create(std::shared_ptr callback) + { + SPXAUDIOSTREAMHANDLE haudioStream = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_stream_create_push_audio_output_stream(&haudioStream)); + + auto stream = new PushAudioOutputStream(haudioStream); + SPX_THROW_ON_FAIL(push_audio_output_stream_set_callbacks(haudioStream, stream, WriteCallbackWrapper, CloseCallbackWrapper)); + stream->m_callback = callback; + + return std::shared_ptr(stream); + } + +protected: + + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + explicit PushAudioOutputStream(SPXAUDIOSTREAMHANDLE haudioStream) : AudioOutputStream(haudioStream) { } + + class FunctionCallbackWrapper : public PushAudioOutputStreamCallback + { + public: + + FunctionCallbackWrapper(WriteCallbackFunction_Type writeCallback, CloseCallbackFunction_Type closeCallback) : + m_writeCallback(writeCallback), + m_closeCallback(closeCallback) + { + }; + + int Write(uint8_t* dataBuffer, uint32_t size) override { return m_writeCallback(dataBuffer, size); } + void Close() override { if (m_closeCallback != nullptr) m_closeCallback(); }; + + private: + + DISABLE_COPY_AND_MOVE(FunctionCallbackWrapper); + + WriteCallbackFunction_Type m_writeCallback; + CloseCallbackFunction_Type m_closeCallback; + }; + + /*! \endcond */ + +private: + + DISABLE_COPY_AND_MOVE(PushAudioOutputStream); + + static int WriteCallbackWrapper(void* pvContext, uint8_t* dataBuffer, uint32_t size) + { + PushAudioOutputStream* ptr = (PushAudioOutputStream*)pvContext; + return ptr->m_callback->Write(dataBuffer, size); + } + + static void CloseCallbackWrapper(void* pvContext) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + PushAudioOutputStream* ptr = (PushAudioOutputStream*)pvContext; + ptr->m_callback->Close(); + } + + std::shared_ptr m_callback; +}; + + +inline std::shared_ptr AudioOutputStream::CreatePullStream() +{ + return PullAudioOutputStream::Create(); +} + +inline std::shared_ptr AudioOutputStream::CreatePushStream(void* pvContext, CUSTOM_AUDIO_PUSH_STREAM_WRITE_CALLBACK writeCallback, CUSTOM_AUDIO_PUSH_STREAM_CLOSE_CALLBACK closeCallback) +{ + return PushAudioOutputStream::Create(pvContext, writeCallback, closeCallback); +} + +inline std::shared_ptr AudioOutputStream::CreatePushStream(WriteCallbackFunction_Type writeCallback, CloseCallbackFunction_Type closeCallback) +{ + return PushAudioOutputStream::Create(writeCallback, closeCallback); +} + +inline std::shared_ptr AudioOutputStream::CreatePushStream(std::shared_ptr callback) +{ + return PushAudioOutputStream::Create(callback); +} + + +} } } } // Microsoft::CognitiveServices::Speech::Audio diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_stream_format.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_stream_format.h new file mode 100644 index 0000000..ea2156b --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_audio_stream_format.h @@ -0,0 +1,215 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_audio_stream_format.h: Public API declarations for AudioStreamFormat and related C++ classes +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Audio { + +/// +/// Defines supported audio stream container format. +/// Changed in version 1.4.0. +/// +enum class AudioStreamContainerFormat +{ + /// + /// Stream ContainerFormat definition for OGG OPUS. + /// + OGG_OPUS = 0x101, + + /// + /// Stream ContainerFormat definition for MP3. + /// + MP3 = 0x102, + + /// + /// Stream ContainerFormat definition for FLAC. Added in version 1.7.0. + /// + FLAC = 0x103, + + /// + /// Stream ContainerFormat definition for ALAW. Added in version 1.7.0. + /// + ALAW = 0x104, + + /// + /// Stream ContainerFormat definition for MULAW. Added in version 1.7.0. + /// + MULAW = 0x105, + + /// + /// Stream ContainerFormat definition for AMRNB. Currently not supported. + /// + AMRNB = 0x106, + + /// + /// Stream ContainerFormat definition for AMRWB. Currently not supported. + /// + AMRWB = 0x107, + + /// + /// Stream ContainerFormat definition for any other or unknown format. + /// + ANY = 0x108 +}; + +/// +/// Represents the format specified inside WAV container. +/// +enum class AudioStreamWaveFormat +{ + /// + /// AudioStreamWaveFormat definition for PCM (pulse-code modulated) data in integer format. + /// + PCM = 0x0001, + + /// + /// AudioStreamWaveFormat definition A-law-encoded format. + /// + ALAW = 0x0006, + + /// + /// AudioStreamWaveFormat definition for Mu-law-encoded format. + /// + MULAW = 0x0007, + + /// + /// AudioStreamWaveFormat definition for G.722-encoded format. + /// + G722 = 0x028F +}; + +/// +/// Class to represent the audio stream format used for custom audio input configurations. +/// Updated in version 1.5.0. +/// +class AudioStreamFormat +{ +public: + + /// + /// Destructor, does nothing. + /// + virtual ~AudioStreamFormat() {} + + /// + /// Internal operator used to get underlying handle value. + /// + /// A handle. + explicit operator SPXAUDIOSTREAMFORMATHANDLE() const { return m_hformat.get(); } + + /// + /// Creates an audio stream format object representing the default audio stream format (16 kHz, 16 bit, mono PCM). + /// + /// A shared pointer to AudioStreamFormat + static std::shared_ptr GetDefaultInputFormat() + { + SPXAUDIOSTREAMFORMATHANDLE hformat = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_stream_format_create_from_default_input(&hformat)); + + auto format = new AudioStreamFormat(hformat); + return std::shared_ptr(format); + } + + /// + /// Creates an audio stream format object with the specified PCM waveformat characteristics. + /// + /// Samples per second. + /// Bits per sample. + /// Number of channels in the waveform-audio data. + /// The format specified inside the WAV container. + /// A shared pointer to AudioStreamFormat + static std::shared_ptr GetWaveFormat(uint32_t samplesPerSecond, uint8_t bitsPerSample, uint8_t channels, AudioStreamWaveFormat waveFormat) + { + SPXAUDIOSTREAMFORMATHANDLE hformat = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_stream_format_create_from_waveformat(&hformat, samplesPerSecond, bitsPerSample, channels, (Audio_Stream_Wave_Format)waveFormat)); + + auto format = new AudioStreamFormat(hformat); + return std::shared_ptr(format); + } + + /// + /// Creates an audio stream format object with the specified PCM waveformat characteristics. + /// + /// Samples per second. + /// Bits per sample. + /// Number of channels in the waveform-audio data. + /// A shared pointer to AudioStreamFormat + static std::shared_ptr GetWaveFormatPCM(uint32_t samplesPerSecond, uint8_t bitsPerSample = 16, uint8_t channels = 1) + { + SPXAUDIOSTREAMFORMATHANDLE hformat = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_stream_format_create_from_waveformat(&hformat, samplesPerSecond, bitsPerSample, channels, Audio_Stream_Wave_Format::StreamWaveFormat_PCM)); + + auto format = new AudioStreamFormat(hformat); + return std::shared_ptr(format); + } + + /// + /// Creates an audio stream format object representing the default audio stream format (16 kHz, 16 bit, mono PCM). + /// Added in version 1.4.0 + /// + /// A shared pointer to AudioStreamFormat + static std::shared_ptr GetDefaultOutputFormat() + { + SPXAUDIOSTREAMFORMATHANDLE hformat = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_stream_format_create_from_default_output(&hformat)); + + auto format = new AudioStreamFormat(hformat); + return std::shared_ptr(format); + } + + /// + /// Creates an audio stream format object with the specified compressed audio container format, to be used as input format. + /// Support added in 1.4.0. + /// + /// + /// Formats are defined in AudioStreamContainerFormat enum. + /// + /// Compressed format type. + /// A shared pointer to AudioStreamFormat. + static std::shared_ptr GetCompressedFormat(AudioStreamContainerFormat compressedFormat) + { + SPXAUDIOSTREAMFORMATHANDLE hformat = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_stream_format_create_from_compressed_format(&hformat, (Audio_Stream_Container_Format)compressedFormat)); + + auto format = new AudioStreamFormat(hformat); + return std::shared_ptr(format); + } + +protected: + + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + explicit AudioStreamFormat(SPXAUDIOSTREAMFORMATHANDLE hformat) : m_hformat(hformat) { } + + /*! \endcond */ + +private: + + DISABLE_COPY_AND_MOVE(AudioStreamFormat); + + /// + /// Internal member variable that holds the smart handle. + /// + SmartHandle m_hformat; +}; + + +} } } } // Microsoft::CognitiveServices::Speech::Audio diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_auto_detect_source_lang_config.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_auto_detect_source_lang_config.h new file mode 100644 index 0000000..e68f3fc --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_auto_detect_source_lang_config.h @@ -0,0 +1,141 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Class that defines auto detection source configuration +/// Updated in 1.13.0 +/// +class AutoDetectSourceLanguageConfig +{ +public: + /// + /// Internal operator used to get underlying handle value. + /// + /// A handle. + explicit operator SPXAUTODETECTSOURCELANGCONFIGHANDLE() const { return m_hconfig; } + + /// + /// Creates an instance of the AutoDetectSourceLanguageConfig with open range as source languages + /// Note: only , embedded speech translation and multilingual support source language auto detection from open range, + /// for , please use AutoDetectSourceLanguageConfig with specific source languages. + /// Added in 1.13.0 + /// + /// A shared pointer to the new AutoDetectSourceLanguageConfig instance. + static std::shared_ptr FromOpenRange() + { + SPXAUTODETECTSOURCELANGCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + + SPX_THROW_ON_FAIL(create_auto_detect_source_lang_config_from_open_range(&hconfig)); + auto ptr = new AutoDetectSourceLanguageConfig(hconfig); + return std::shared_ptr(ptr); + } + + /// + /// Creates an instance of the AutoDetectSourceLanguageConfig with source languages + /// + /// The list of source languages. + /// A shared pointer to the new AutoDetectSourceLanguageConfig instance. + static std::shared_ptr FromLanguages(const std::vector& languages) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, languages.empty()); + SPXAUTODETECTSOURCELANGCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + + std::string languagesStr; + bool isFirst = true; + for (const SPXSTRING& language : languages) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, language.empty()); + if (!isFirst) + { + languagesStr += ","; + } + isFirst = false; + languagesStr += Utils::ToUTF8(language); + } + SPX_THROW_ON_FAIL(create_auto_detect_source_lang_config_from_languages(&hconfig, languagesStr.c_str())); + auto ptr = new AutoDetectSourceLanguageConfig(hconfig); + return std::shared_ptr(ptr); + } + + /// + /// Creates an instance of the AutoDetectSourceLanguageConfig with a list of source language config + /// + /// The list of source languages config + /// A shared pointer to the new AutoDetectSourceLanguageConfig instance. + static std::shared_ptr FromSourceLanguageConfigs(std::vector> configList) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, configList.empty()); + SPXAUTODETECTSOURCELANGCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + + bool isFirst = true; + for (const auto& config : configList) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, config == nullptr); + if (isFirst) + { + SPX_THROW_ON_FAIL(create_auto_detect_source_lang_config_from_source_lang_config(&hconfig, Utils::HandleOrInvalid(config))); + isFirst = false; + } + else + { + SPX_THROW_ON_FAIL(add_source_lang_config_to_auto_detect_source_lang_config(hconfig, Utils::HandleOrInvalid(config))); + } + } + auto ptr = new AutoDetectSourceLanguageConfig(hconfig); + return std::shared_ptr(ptr); + } + + /// + /// Destructs the object. + /// + virtual ~AutoDetectSourceLanguageConfig() + { + auto_detect_source_lang_config_release(m_hconfig); + property_bag_release(m_propertybag); + } + +private: + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + explicit AutoDetectSourceLanguageConfig(SPXAUTODETECTSOURCELANGCONFIGHANDLE hconfig) + :m_hconfig(hconfig) + { + SPX_THROW_ON_FAIL(auto_detect_source_lang_config_get_property_bag(hconfig, &m_propertybag)); + } + + /// + /// Internal member variable that holds the config + /// + SPXAUTODETECTSOURCELANGCONFIGHANDLE m_hconfig; + + /// + /// Internal member variable that holds the properties of the speech config + /// + SPXPROPERTYBAGHANDLE m_propertybag; + + DISABLE_COPY_AND_MOVE(AutoDetectSourceLanguageConfig); +}; + +}}} + diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_auto_detect_source_lang_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_auto_detect_source_lang_result.h new file mode 100644 index 0000000..538ea58 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_auto_detect_source_lang_result.h @@ -0,0 +1,85 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Contains auto detected source language result +/// Added in 1.8.0 +/// +class AutoDetectSourceLanguageResult +{ +public: + + /// + /// Creates an instance of AutoDetectSourceLanguageResult object for the speech recognition result. + /// + /// The speech recognition result. + /// A shared pointer to AutoDetectSourceLanguageResult. + static std::shared_ptr FromResult(std::shared_ptr result) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, result == nullptr); + auto ptr = new AutoDetectSourceLanguageResult(result); + return std::shared_ptr(ptr); + } + + /// + /// Creates an instance of AutoDetectSourceLanguageResult object for the speech translation result. + /// + /// The speech translation result. + /// A shared pointer to AutoDetectSourceLanguageResult. + static std::shared_ptr FromResult(std::shared_ptr result) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, result == nullptr); + auto ptr = new AutoDetectSourceLanguageResult(result); + return std::shared_ptr(ptr); + } + + /// + /// Creates an instance of AutoDetectSourceLanguageResult object for the convesation transcription result. + /// + /// The conversation transcription result. + /// A shared pointer to AutoDetectSourceLanguageResult. + static std::shared_ptr FromResult(std::shared_ptr result) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, result == nullptr); + auto ptr = new AutoDetectSourceLanguageResult(result); + return std::shared_ptr(ptr); + } + + /// + /// The language value + /// If this is empty, it means the system fails to detect the source language automatically + /// + const SPXSTRING Language; + +protected: + + /*! \cond PROTECTED */ + // Using RecognitionResult pointer, so this can cover all classes that inherit from RecognitionResult + AutoDetectSourceLanguageResult(std::shared_ptr result) : + Language(result->Properties.GetProperty(PropertyId::SpeechServiceConnection_AutoDetectSourceLanguageResult)) + { + } + + /*! \endcond */ + +private: + + DISABLE_DEFAULT_CTORS(AutoDetectSourceLanguageResult); +}; + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_class_language_model.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_class_language_model.h new file mode 100644 index 0000000..a3099cc --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_class_language_model.h @@ -0,0 +1,70 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_class_language_model.h: Public API declarations for ClassLanguageModel C++ class +// + +#pragma once +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + + +/// +/// Represents a list of grammars for dynamic grammar scenarios. +/// Added in version 1.7.0. +/// +/// +/// ClassLanguageModels are only usable in specific scenarios and are not generally available. +/// +class ClassLanguageModel : public Grammar +{ +public: + + /// + /// Creates a class language model from a storage ID. + /// + /// The persisted storage ID of the language model. + /// The grammar list associated with the recognizer. + /// + /// Creating a ClassLanguageModel from a storage ID is only usable in specific scenarios and is not generally available. + /// + static std::shared_ptr FromStorageId(const SPXSTRING& storageId) + { + SPXGRAMMARHANDLE hgrammar = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(class_language_model_from_storage_id(&hgrammar, Utils::ToUTF8(storageId.c_str()))); + + return std::make_shared(hgrammar); + } + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Class Language Model handle. + explicit ClassLanguageModel(SPXGRAMMARHANDLE hgrammar = SPXHANDLE_INVALID) : Grammar(hgrammar) { } + + /// + /// Assigns a grammar to a class in the language mode. + /// + /// Name of the class to assign the grammar to. + /// Grammar to assign. + template + void AssignClass(const SPXSTRING& className, std::shared_ptr grammar) + { + SPX_THROW_ON_FAIL(class_language_model_assign_class(m_hgrammar.get(), Utils::ToUTF8(className.c_str()), (SPXPHRASEHANDLE)(*grammar.get()))); + } + +private: + + DISABLE_COPY_AND_MOVE(ClassLanguageModel); +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_common.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_common.h new file mode 100644 index 0000000..2e8d382 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_common.h @@ -0,0 +1,16 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_common.h: Public API declarations for global C++ APIs/namespaces +// + +#pragma once + +#include +#include +#include +#include // must include after spxdebug.h or speechapi*.h (can NOT be included before) + +#define DISABLE_COPY_AND_MOVE(T) AZAC_DISABLE_COPY_AND_MOVE(T) +#define DISABLE_DEFAULT_CTORS(T) AZAC_DISABLE_DEFAULT_CTORS(T) diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_connection.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_connection.h new file mode 100644 index 0000000..c072254 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_connection.h @@ -0,0 +1,346 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Connection is a proxy class for managing connection to the speech service of the specified Recognizer. +/// By default, a Recognizer autonomously manages connection to service when needed. +/// The Connection class provides additional methods for users to explicitly open or close a connection and +/// to subscribe to connection status changes. +/// The use of Connection is optional. It is intended for scenarios where fine tuning of application +/// behavior based on connection status is needed. Users can optionally call Open() to manually +/// initiate a service connection before starting recognition on the Recognizer associated with this Connection. +/// After starting a recognition, calling Open() or Close() might fail. This will not impact +/// the Recognizer or the ongoing recognition. Connection might drop for various reasons, the Recognizer will +/// always try to reinstitute the connection as required to guarantee ongoing operations. In all these cases +/// Connected/Disconnected events will indicate the change of the connection status. +/// Updated in version 1.17.0. +/// +class Connection : public std::enable_shared_from_this +{ + +public: + /// + /// Gets the Connection instance from the specified recognizer. + /// + /// The recognizer associated with the connection. + /// The Connection instance of the recognizer. + static std::shared_ptr FromRecognizer(std::shared_ptr recognizer) + { + SPX_INIT_HR(hr); + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, recognizer == nullptr); + + SPXCONNECTIONHANDLE handle = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(hr = ::connection_from_recognizer(recognizer->m_hreco, &handle)); + + return std::make_shared(handle); + } + + /// + /// Gets the Connection instance from the specified conversation translator. + /// + /// The conversation translator associated with the connection. + /// The Connection instance of the conversation translator. + static std::shared_ptr FromConversationTranslator(std::shared_ptr convTrans) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, convTrans == nullptr); + + SPXCONNECTIONHANDLE handle = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::connection_from_conversation_translator(convTrans->m_handle, &handle)); + + return std::make_shared(handle); + } + + /// + /// Gets the Connection instance from the specified dialog service connector, used for observing and managing + /// connection and disconnection from the speech service. + /// + /// The dialog service connector associated with the connection. + /// The Connection instance of the dialog service connector. + static std::shared_ptr FromDialogServiceConnector(std::shared_ptr dialogServiceConnector) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, dialogServiceConnector == nullptr); + + SPXCONNECTIONHANDLE handle = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::connection_from_dialog_service_connector(dialogServiceConnector->m_handle, &handle)); + + return std::make_shared(handle); + } + + /// + /// Gets the Connection instance from the specified speech synthesizer. + /// Added in version 1.17.0 + /// + /// The speech synthesizer associated with the connection. + /// The Connection instance of the speech synthesizer. + static std::shared_ptr FromSpeechSynthesizer(std::shared_ptr synthesizer) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, synthesizer == nullptr); + + SPXCONNECTIONHANDLE handle = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::connection_from_speech_synthesizer(synthesizer->m_hsynth, &handle)); + + return std::make_shared(handle); + } + + /// + /// Starts to set up connection to the service. + /// Users can optionally call Open() to manually set up a connection in advance before starting recognition/synthesis on the + /// Recognizer/Synthesizer associated with this Connection. After starting recognition, calling Open() might fail, depending on + /// the process state of the Recognizer/Synthesizer. But the failure does not affect the state of the associated Recognizer/Synthesizer. + /// Note: On return, the connection might not be ready yet. Please subscribe to the Connected event to + /// be notified when the connection is established. + /// + /// Indicates whether the connection is used for continuous recognition or single-shot recognition. It takes no effect if the connection is from SpeechSynthsizer. + void Open(bool forContinuousRecognition) + { + SPX_THROW_HR_IF(SPXERR_INVALID_HANDLE, m_connectionHandle == SPXHANDLE_INVALID); + SPX_THROW_ON_FAIL(::connection_open(m_connectionHandle, forContinuousRecognition)); + } + + /// + /// Closes the connection the service. + /// Users can optionally call Close() to manually shutdown the connection of the associated Recognizer/Synthesizer. The call + /// might fail, depending on the process state of the Recognizer/Synthesizer. But the failure does not affect the state of the + /// associated Recognizer/Synthesizer. + /// + void Close() + { + SPX_THROW_HR_IF(SPXERR_INVALID_HANDLE, m_connectionHandle == SPXHANDLE_INVALID); + SPX_THROW_ON_FAIL(::connection_close(m_connectionHandle)); + } + + /// + /// Appends a parameter in a message to service. + /// Added in version 1.7.0. + /// + /// the message path. + /// Name of the property. + /// Value of the property. This is a json string. + /// void. + void SetMessageProperty(const SPXSTRING& path, const SPXSTRING& propertyName, const SPXSTRING& propertyValue) + { + SPX_THROW_HR_IF(SPXERR_INVALID_HANDLE, m_connectionHandle == SPXHANDLE_INVALID); + SPX_THROW_ON_FAIL(::connection_set_message_property(m_connectionHandle, Utils::ToUTF8(path).c_str(), Utils::ToUTF8(propertyName).c_str(), Utils::ToUTF8(propertyValue).c_str())); + } + + /// + /// Send a message to the speech service. + /// Added in version 1.7.0. + /// + /// The path of the message. + /// The payload of the message. This is a json string. + /// An empty future. + std::future SendMessageAsync(const SPXSTRING& path, const SPXSTRING& payload) + { + auto keep_alive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keep_alive, this, path, payload]() -> void { + SPX_THROW_HR_IF(SPXERR_INVALID_HANDLE, m_connectionHandle == SPXHANDLE_INVALID); + SPX_THROW_ON_FAIL(::connection_send_message(m_connectionHandle, Utils::ToUTF8(path.c_str()), Utils::ToUTF8(payload.c_str()))); + }); + return future; + } + + /// + /// Send a binary message to the speech service. + /// This method doesn't work for the connection of SpeechSynthesizer. + /// Added in version 1.10.0. + /// + /// The path of the message. + /// The binary payload of the message. + /// The size of the binary payload. + /// An empty future. + std::future SendMessageAsync(const SPXSTRING& path, uint8_t* payload, uint32_t size) + { + auto keep_alive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keep_alive, this, path, payload, size]() -> void { + SPX_THROW_HR_IF(SPXERR_INVALID_HANDLE, m_connectionHandle == SPXHANDLE_INVALID); + SPX_THROW_ON_FAIL(::connection_send_message_data(m_connectionHandle, Utils::ToUTF8(path.c_str()), payload, size)); + }); + return future; + } + + /// + /// The Connected event to indicate that the recognizer is connected to service. + /// + EventSignal Connected; + + /// + /// The Disconnected event to indicate that the recognizer is disconnected from service. + /// + EventSignal Disconnected; + + /// + /// The MessageReceived event to indicate that the underlying protocol received a message from the service. + /// Added in version 1.10.0. + /// + EventSignal MessageReceived; + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// The connection handle. + explicit Connection(SPXCONNECTIONHANDLE handle) : + Connected(GetConnectionEventConnectionsChangedCallback(), GetConnectionEventConnectionsChangedCallback()), + Disconnected(GetConnectionEventConnectionsChangedCallback(), GetConnectionEventConnectionsChangedCallback()), + MessageReceived(GetConnectionMessageEventConnectionsChangedCallback(), GetConnectionMessageEventConnectionsChangedCallback()), + m_connectionHandle(handle) + { + SPX_DBG_TRACE_FUNCTION(); + } + + /// + /// Destructor. + /// + ~Connection() + { + SPX_DBG_TRACE_FUNCTION(); + + try + { + Disconnected.DisconnectAll(); + Connected.DisconnectAll(); + } + catch (const std::exception& ex) + { + SPX_TRACE_ERROR("Exception caught in ~Connection(): %s", ex.what()); + (void)ex; + } + catch (...) + { + SPX_TRACE_ERROR("Unknown exception happened during ~Connection()."); + } + + if (m_connectionHandle != SPXHANDLE_INVALID) + { + ::connection_handle_release(m_connectionHandle); + m_connectionHandle = SPXHANDLE_INVALID; + } + } + +private: + DISABLE_COPY_AND_MOVE(Connection); + + SPXCONNECTIONHANDLE m_connectionHandle; + + static void FireConnectionEvent(bool firingConnectedEvent, SPXEVENTHANDLE event, void* context) + { + std::exception_ptr p; + try + { + std::unique_ptr connectionEvent{ new ConnectionEventArgs(event) }; + + auto connection = static_cast(context); + auto keepAlive = connection->shared_from_this(); + if (firingConnectedEvent) + { + connection->Connected.Signal(*connectionEvent.get()); + } + else + { + connection->Disconnected.Signal(*connectionEvent.get()); + } + } + +#ifdef SHOULD_HANDLE_FORCED_UNWIND + // Currently Python forcibly kills the thread by throwing __forced_unwind, + // taking care we propagate this exception further. + catch (abi::__forced_unwind&) + { + SPX_TRACE_ERROR("__forced_unwind exception caught in FireConnectionEvent."); + throw; + } +#endif + catch (...) + { + if (recognizer_event_handle_is_valid(event)) { + recognizer_event_handle_release(event); + } + SPX_TRACE_ERROR("Caught exception in FireConnectionEvent(%s). Will rethrow later.", firingConnectedEvent ? "Connected" : "Disconnected"); + throw; + } + + // ConnectionEventArgs doesn't hold hevent, and thus can't release it properly ... release it here + SPX_DBG_ASSERT(recognizer_event_handle_is_valid(event)); + recognizer_event_handle_release(event); + } + + static void FireEvent_Connected(SPXEVENTHANDLE event, void* context) + { + FireConnectionEvent(true, event, context); + } + + static void FireEvent_Disconnected(SPXEVENTHANDLE event, void* context) + { + FireConnectionEvent(false, event, context); + } + + static void FireEvent_MessageReceived(SPXEVENTHANDLE event, void* context) + { + std::unique_ptr connectionEvent { new ConnectionMessageEventArgs(event) }; + + auto connection = static_cast(context); + auto keepAlive = connection->shared_from_this(); + connection->MessageReceived.Signal(*connectionEvent.get()); + } + + void ConnectionEventConnectionsChanged(const EventSignal& connectionEvent) + { + if (m_connectionHandle != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_connectionHandle=0x%8p", __FUNCTION__, (void*)m_connectionHandle); + SPX_DBG_TRACE_VERBOSE_IF(!::connection_handle_is_valid(m_connectionHandle), "%s: m_connectionHandle is INVALID!!!", __FUNCTION__); + + if (&connectionEvent == &Connected) + { + SPX_THROW_ON_FAIL(connection_connected_set_callback(m_connectionHandle, Connected.IsConnected() ? FireEvent_Connected : nullptr, this)); + } + else if (&connectionEvent == &Disconnected) + { + SPX_THROW_ON_FAIL(connection_disconnected_set_callback(m_connectionHandle, Disconnected.IsConnected() ? FireEvent_Disconnected : nullptr, this)); + } + } + } + + void ConnectionMessageEventConnectionsChanged(const EventSignal& connectionEvent) + { + if (m_connectionHandle != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_connectionHandle=0x%8p", __FUNCTION__, (void*)m_connectionHandle); + SPX_DBG_TRACE_VERBOSE_IF(!::connection_handle_is_valid(m_connectionHandle), "%s: m_connectionHandle is INVALID!!!", __FUNCTION__); + + if (&connectionEvent == &MessageReceived) + { + SPX_THROW_ON_FAIL(connection_message_received_set_callback(m_connectionHandle, MessageReceived.IsConnected() ? FireEvent_MessageReceived : nullptr, this)); + } + } + } + + inline std::function&)> GetConnectionEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& connectionEvent) { this->ConnectionEventConnectionsChanged(connectionEvent); }; + } + + inline std::function&)> GetConnectionMessageEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& connectionEvent) { this->ConnectionMessageEventConnectionsChanged(connectionEvent); }; + } +}; + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_connection_eventargs.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_connection_eventargs.h new file mode 100644 index 0000000..1e56f25 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_connection_eventargs.h @@ -0,0 +1,68 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + + +/// +/// Provides data for the ConnectionEvent. +/// Added in version 1.2.0. +/// +class ConnectionEventArgs : public SessionEventArgs +{ +protected: + /*! \cond PRIVATE */ + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXEVENTHANDLE hevent) : + PropertyCollection([=]() + { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + recognizer_connection_event_get_property_bag(hevent, &hpropbag); + return hpropbag; + }()) + {} + }; + + PrivatePropertyCollection m_properties; + /*! \endcond */ + +public: + + /// + /// Constructor. + /// + /// Event handle. + explicit ConnectionEventArgs(SPXEVENTHANDLE hevent) : + SessionEventArgs(hevent), + m_properties(hevent), + Properties(m_properties) + { + }; + + /// + virtual ~ConnectionEventArgs() {} + + /// + /// Collection of additional properties. + /// + const PropertyCollection& Properties; + +private: + + DISABLE_COPY_AND_MOVE(ConnectionEventArgs); +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_connection_message.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_connection_message.h new file mode 100644 index 0000000..a1a9469 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_connection_message.h @@ -0,0 +1,152 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_connection_message.h: Public API declarations for ConnectionMessage C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + + +/// +/// ConnectionMessage represents implementation specific messages sent to and received from +/// the speech service. These messages are provided for debugging purposes and should not +/// be used for production use cases with the Azure Cognitive Services Speech Service. +/// Messages sent to and received from the Speech Service are subject to change without +/// notice. This includes message contents, headers, payloads, ordering, etc. +/// Added in version 1.10.0. +/// +class ConnectionMessage +{ +private: + + /*! \cond PRIVATE */ + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXCONNECTIONMESSAGEHANDLE hcm) : + PropertyCollection( + [=]() { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + ::connection_message_get_property_bag(hcm, &hpropbag); + return hpropbag; + }()) + { + } + }; + + SPXCONNECTIONMESSAGEHANDLE m_hcm; + PrivatePropertyCollection m_properties; + + /*! \endcond */ + +public: + + /// + /// Constructor. + /// + /// Event handle. + explicit ConnectionMessage(SPXCONNECTIONMESSAGEHANDLE hcm) : + m_hcm(hcm), + m_properties(hcm), + Properties(m_properties) + { + }; + + /// + /// Destructor. + /// + virtual ~ConnectionMessage() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hcm); + SPX_THROW_ON_FAIL(::connection_message_handle_release(m_hcm)); + } + + /// + /// Gets the message path. + /// + /// An std::string containing the message path. + std::string GetPath() const + { + SPX_THROW_HR_IF(SPXERR_INVALID_HANDLE, m_hcm == SPXHANDLE_INVALID); + return m_properties.GetProperty("connection.message.path"); + } + + /// + /// Checks to see if the ConnectionMessage is a text message. + /// See also IsBinaryMessage(). + /// + /// A bool indicated if the message payload is text. + bool IsTextMessage() const + { + SPX_THROW_HR_IF(SPXERR_INVALID_HANDLE, m_hcm == SPXHANDLE_INVALID); + return m_properties.GetProperty("connection.message.type") == "text"; + } + + /// + /// Checks to see if the ConnectionMessage is a binary message. + /// See also GetBinaryMessage(). + /// + /// A bool indicated if the message payload is binary. + bool IsBinaryMessage() const + { + SPX_THROW_HR_IF(SPXERR_INVALID_HANDLE, m_hcm == SPXHANDLE_INVALID); + return m_properties.GetProperty("connection.message.type") == "binary"; + } + + /// + /// Gets the text message payload. Typically the text message content-type is + /// application/json. To determine other content-types use + /// Properties.GetProperty("Content-Type"). + /// + /// An std::string containing the text message. + std::string GetTextMessage() const + { + SPX_THROW_HR_IF(SPXERR_INVALID_HANDLE, m_hcm == SPXHANDLE_INVALID); + return m_properties.GetProperty("connection.message.text.message"); + } + + /// + /// Gets the binary message payload. + /// + /// An std::vector containing the binary message. + std::vector GetBinaryMessage() const + { + SPX_THROW_HR_IF(SPXERR_INVALID_HANDLE, m_hcm == SPXHANDLE_INVALID); + auto size = ::connection_message_get_data_size(m_hcm); + + std::vector message(size); + SPX_THROW_ON_FAIL(::connection_message_get_data(m_hcm, message.data(), size)); + + return message; + } + + /// + /// A collection of properties and their values defined for this . + /// Message headers can be accessed via this collection (e.g. "Content-Type"). + /// + PropertyCollection& Properties; + +private: + + /*! \cond PRIVATE */ + + DISABLE_COPY_AND_MOVE(ConnectionMessage); + + /*! \endcond */ +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_connection_message_eventargs.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_connection_message_eventargs.h new file mode 100644 index 0000000..3ff6f79 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_connection_message_eventargs.h @@ -0,0 +1,79 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_connection_message_eventargs.h: Public API declarations for ConnectionMessageEventArgs C++ base class +// + +#pragma once +#include +#include +#include +#include +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + + +/// +/// Provides data for the ConnectionMessageEvent +/// +class ConnectionMessageEventArgs : public EventArgs +{ +private: + + /*! \cond PRIVATE */ + + SPXEVENTHANDLE m_hevent; + std::shared_ptr m_message; + + /*! \endcond */ + +public: + + /// + /// Constructor. Creates a new instance using the provided handle. + /// + /// Event handle. + explicit ConnectionMessageEventArgs(SPXEVENTHANDLE hevent) : + m_hevent(hevent), + m_message(std::make_shared(MessageHandleFromEventHandle(hevent))) + { + }; + + /// + /// Destructor. + /// + virtual ~ConnectionMessageEventArgs() + { + SPX_THROW_ON_FAIL(::connection_message_received_event_handle_release(m_hevent)); + } + + /// + /// Gets the associated with this . + /// + /// An `std::shared` containing the message. + std::shared_ptr GetMessage() const { return m_message; } + +private: + + /*! \cond PRIVATE */ + + DISABLE_COPY_AND_MOVE(ConnectionMessageEventArgs); + + SPXCONNECTIONMESSAGEHANDLE MessageHandleFromEventHandle(SPXEVENTHANDLE hevent) + { + SPXCONNECTIONMESSAGEHANDLE hcm = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::connection_message_received_event_get_message(hevent, &hcm)); + return hcm; + } + + /*! \endcond */ + +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation.h new file mode 100644 index 0000000..339f22c --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation.h @@ -0,0 +1,340 @@ + +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_conversation.h: Public API declarations for Conversation C++ class +// + +#pragma once +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Transcription { + +/// +/// Class for conversation. +/// Added in version 1.8.0 +/// +class Conversation : public std::enable_shared_from_this +{ +public: + + static constexpr size_t MAX_CONVERSATION_ID_LEN = 1024; + + /// + /// Create a conversation using a speech config and an optional conversation id. + /// + /// A shared smart pointer of a speech config object. + /// Conversation Id. + /// A shared smart pointer of the created conversation object. + static std::future> CreateConversationAsync(std::shared_ptr speechConfig, const SPXSTRING& conversationId = SPXSTRING()) + { + auto future = std::async(std::launch::async, [conversationId, speechConfig]() -> std::shared_ptr { + SPXCONVERSATIONHANDLE hconversation; + SPX_THROW_ON_FAIL(conversation_create_from_config(&hconversation, (SPXSPEECHCONFIGHANDLE)(*speechConfig), Utils::ToUTF8(conversationId).c_str())); + return std::make_shared(hconversation); + }); + return future; + } + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Recognizer handle. + explicit Conversation(SPXCONVERSATIONHANDLE hconversation) : + m_hconversation(hconversation), + m_properties(hconversation), + Properties(m_properties) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + } + + /// + /// Destructor. + /// + ~Conversation() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + ::conversation_release_handle(m_hconversation); + m_hconversation = SPXHANDLE_INVALID; + } + + /// + /// Internal operator used to get underlying handle value. + /// + /// A handle. + explicit operator SPXCONVERSATIONHANDLE () const { return m_hconversation; } + + /// + /// Get the conversation id. + /// + /// Conversation id. + SPXSTRING GetConversationId() + { + char id[MAX_CONVERSATION_ID_LEN + 1]; + std::memset(id, 0, MAX_CONVERSATION_ID_LEN); + SPX_THROW_ON_FAIL(conversation_get_conversation_id(m_hconversation, id, MAX_CONVERSATION_ID_LEN)); + return id; + } + + /// + /// Add a participant to a conversation using the user's id. + /// + /// Note: The returned participant can be used to remove. If the client changes the participant's attributes, + /// the changed attributes are passed on to the service only when the participant is added again. + /// + /// A user id. + /// a shared smart pointer of the participant. + std::future> AddParticipantAsync(const SPXSTRING& userId) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this, userId]() -> std::shared_ptr { + const auto participant = Participant::From(userId); + SPX_THROW_ON_FAIL(conversation_update_participant(m_hconversation, true, (SPXPARTICIPANTHANDLE)(*participant))); + return participant; + }); + return future; + } + + /// + /// Add a participant to a conversation using the User object. + /// + /// A shared smart pointer to a User object. + /// The passed in User object. + std::future> AddParticipantAsync(const std::shared_ptr& user) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this, user]() -> std::shared_ptr { + SPX_THROW_ON_FAIL(conversation_update_participant_by_user(m_hconversation, true, (SPXUSERHANDLE)(*user))); + return user; + }); + return future; + } + + /// + /// Add a participant to a conversation using the participant object + /// + /// A shared smart pointer to a participant object. + /// The passed in participant object. + std::future> AddParticipantAsync(const std::shared_ptr& participant) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this, participant]() -> std::shared_ptr { + SPX_THROW_ON_FAIL(conversation_update_participant(m_hconversation, true, (SPXPARTICIPANTHANDLE)(*participant))); + return participant; + }); + return future; + } + + /// + /// Remove a participant from a conversation using the participant object + /// + /// A shared smart pointer of a participant object. + /// An empty future. + std::future RemoveParticipantAsync(const std::shared_ptr& participant) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this, participant]() -> void { + SPX_THROW_ON_FAIL(conversation_update_participant(m_hconversation, false, (SPXPARTICIPANTHANDLE)(*participant))); + }); + return future; + } + + /// + /// Remove a participant from a conversation using the User object + /// + /// A smart pointer of a User. + /// An empty future. + std::future RemoveParticipantAsync(const std::shared_ptr& user) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this, user]() -> void { + SPX_THROW_ON_FAIL(conversation_update_participant_by_user(m_hconversation, false, SPXUSERHANDLE(*user))); + }); + return future; + } + + /// + /// Remove a participant from a conversation using a user id string. + /// + /// A user id. + /// An empty future. + std::future RemoveParticipantAsync(const SPXSTRING& userId) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this, userId]() -> void { + SPX_THROW_ON_FAIL(conversation_update_participant_by_user_id(m_hconversation, false, Utils::ToUTF8(userId.c_str()))); + }); + return future; + } + + /// + /// Ends the current conversation. + /// + /// An empty future. + std::future EndConversationAsync() + { + return RunAsync(::conversation_end_conversation); + } + + /// + /// Sets the authorization token that will be used for connecting the server. + /// + /// The authorization token. + void SetAuthorizationToken(const SPXSTRING& token) + { + Properties.SetProperty(PropertyId::SpeechServiceAuthorization_Token, token); + } + + /// + /// Gets the authorization token. + /// + /// Authorization token + SPXSTRING GetAuthorizationToken() + { + return Properties.GetProperty(PropertyId::SpeechServiceAuthorization_Token, SPXSTRING()); + } + + /// + /// Start the conversation. + /// + /// An empty future. + std::future StartConversationAsync() + { + return RunAsync(::conversation_start_conversation); + } + + /// + /// Deletes the conversation. Any participants that are still part of the converation + /// will be ejected after this call. + /// + /// An empty future. + std::future DeleteConversationAsync() + { + return RunAsync(::conversation_delete_conversation); + } + + /// + /// Locks the conversation. After this no new participants will be able to join. + /// + /// An empty future. + std::future LockConversationAsync() + { + return RunAsync(::conversation_lock_conversation); + } + + /// + /// Unlocks the conversation. + /// + /// An empty future. + std::future UnlockConversationAsync() + { + return RunAsync(::conversation_unlock_conversation); + } + + /// + /// Mutes all participants except for the host. This prevents others from generating + /// transcriptions, or sending text messages. + /// + /// An empty future. + std::future MuteAllParticipantsAsync() + { + return RunAsync(::conversation_mute_all_participants); + } + + /// + /// Allows other participants to generate transcriptions, or send text messages. + /// + /// An empty future. + std::future UnmuteAllParticipantsAsync() + { + return RunAsync(::conversation_unmute_all_participants); + } + + /// + /// Mutes a particular participant. This will prevent them generating new transcriptions, + /// or sending text messages. + /// + /// The identifier for the participant. + /// An empty future. + std::future MuteParticipantAsync(const SPXSTRING& participantId) + { + return RunAsync([participantId = Utils::ToUTF8(participantId)](auto handle) + { + return ::conversation_mute_participant(handle, participantId.c_str()); + }); + } + + /// + /// Unmutes a particular participant. + /// + /// The identifier for the participant. + /// An empty future. + std::future UnmuteParticipantAsync(const SPXSTRING& participantId) + { + return RunAsync([participantId = Utils::ToUTF8(participantId)](auto handle) + { + return ::conversation_unmute_participant(handle, participantId.c_str()); + }); + } + +private: + + /*! \cond PRIVATE */ + + SPXCONVERSATIONHANDLE m_hconversation; + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXCONVERSATIONHANDLE hconv) : + PropertyCollection( + [=]() { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + conversation_get_property_bag(hconv, &hpropbag); + return hpropbag; + }()) + { + } + }; + + PrivatePropertyCollection m_properties; + + inline std::future RunAsync(std::function func) + { + auto keepalive = this->shared_from_this(); + return std::async(std::launch::async, [keepalive, this, func]() + { + SPX_THROW_ON_FAIL(func(m_hconversation)); + }); + } + + /*! \endcond */ + +public: + /// + /// A collection of properties and their values defined for this . + /// + PropertyCollection& Properties; + +}; + +}}}} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_transcriber.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_transcriber.h new file mode 100644 index 0000000..8d376a4 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_transcriber.h @@ -0,0 +1,509 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_conversation_transcriber.h: Public API declarations for ConversationTranscriber C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Transcription { + +class Session; + +/// +/// Class for ConversationTranscribers. +/// +class ConversationTranscriber final : public Recognizer +{ +public: + /// + /// Create a conversation transcriber from a speech config + /// + /// Speech configuration. + /// A smart pointer wrapped conversation transcriber pointer. + static std::shared_ptr FromConfig(std::shared_ptr speechconfig, std::nullptr_t) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_conversation_transcriber_from_config( + &hreco, + Utils::HandleOrInvalid(speechconfig), + Utils::HandleOrInvalid(nullptr))); + return std::make_shared(hreco); + } + + /// + /// Create a conversation transcriber from a speech config and audio config. + /// + /// Speech configuration. + /// Audio configuration. + /// A smart pointer wrapped conversation transcriber pointer. + static std::shared_ptr FromConfig(std::shared_ptr speechconfig, std::shared_ptr audioInput = nullptr) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_conversation_transcriber_from_config( + &hreco, + Utils::HandleOrInvalid(speechconfig), + Utils::HandleOrInvalid(audioInput))); + return std::make_shared(hreco); + } + + /// + /// Create a conversation transcriber from a speech config, auto detection source language config and audio config + /// + /// Speech configuration. + /// Auto detection source language config. + /// Audio configuration. + /// A smart pointer wrapped conversation trasncriber pointer. + static std::shared_ptr FromConfig( + std::shared_ptr speechconfig, + std::shared_ptr autoDetectSourceLangConfig, + std::shared_ptr audioInput = nullptr) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_conversation_transcriber_from_auto_detect_source_lang_config( + &hreco, + Utils::HandleOrInvalid(speechconfig), + Utils::HandleOrInvalid(autoDetectSourceLangConfig), + Utils::HandleOrInvalid(audioInput))); + return std::make_shared(hreco); + } + + /// + /// Create a conversation transcriber from a speech config, source language config and audio config + /// + /// Speech configuration. + /// Source language config. + /// Audio configuration. + /// A smart pointer wrapped conversation transcriber pointer. + static std::shared_ptr FromConfig( + std::shared_ptr speechconfig, + std::shared_ptr sourceLanguageConfig, + std::shared_ptr audioInput = nullptr) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_conversation_transcriber_from_source_lang_config( + &hreco, + Utils::HandleOrInvalid(speechconfig), + Utils::HandleOrInvalid(sourceLanguageConfig), + Utils::HandleOrInvalid(audioInput))); + return std::make_shared(hreco); + } + + /// + /// Create a conversation transcriber from a speech config, source language and audio config + /// + /// Speech configuration. + /// Source language. + /// Audio configuration. + /// A smart pointer wrapped conversation transcriber pointer. + static std::shared_ptr FromConfig( + std::shared_ptr speechconfig, + const SPXSTRING& sourceLanguage, + std::shared_ptr audioInput = nullptr) + { + return FromConfig(speechconfig, SourceLanguageConfig::FromLanguage(sourceLanguage), audioInput); + } + + /// + /// Asynchronously starts a conversation transcribing. + /// + /// An empty future. + std::future StartTranscribingAsync() + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this]() -> void { + SPX_INIT_HR(hr); + SPX_THROW_ON_FAIL(hr = recognizer_async_handle_release(m_hasyncStartContinuous)); // close any unfinished previous attempt + + SPX_EXITFN_ON_FAIL(hr = recognizer_start_continuous_recognition_async(m_hreco, &m_hasyncStartContinuous)); + SPX_EXITFN_ON_FAIL(hr = recognizer_start_continuous_recognition_async_wait_for(m_hasyncStartContinuous, UINT32_MAX)); + + SPX_EXITFN_CLEANUP: + auto releaseHr = recognizer_async_handle_release(m_hasyncStartContinuous); + SPX_REPORT_ON_FAIL(releaseHr); + m_hasyncStartContinuous = SPXHANDLE_INVALID; + + SPX_THROW_ON_FAIL(hr); + }); + + return future; + } + + /// + /// Asynchronously stops a conversation transcribing. + /// + /// An empty future. + std::future StopTranscribingAsync() + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this]() -> void { + SPX_INIT_HR(hr); + SPX_THROW_ON_FAIL(hr = recognizer_async_handle_release(m_hasyncStopContinuous)); // close any unfinished previous attempt + + SPX_EXITFN_ON_FAIL(hr = recognizer_stop_continuous_recognition_async(m_hreco, &m_hasyncStopContinuous)); + SPX_EXITFN_ON_FAIL(hr = recognizer_stop_continuous_recognition_async_wait_for(m_hasyncStopContinuous, UINT32_MAX)); + + SPX_EXITFN_CLEANUP: + auto releaseHr = recognizer_async_handle_release(m_hasyncStopContinuous); + SPX_REPORT_ON_FAIL(releaseHr); + m_hasyncStopContinuous = SPXHANDLE_INVALID; + + SPX_THROW_ON_FAIL(hr); + }); + + return future; + } + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Recognizer handle. + explicit ConversationTranscriber(SPXRECOHANDLE hreco) throw() : + Recognizer(hreco), + SessionStarted(GetSessionEventConnectionsChangedCallback()), + SessionStopped(GetSessionEventConnectionsChangedCallback()), + SpeechStartDetected(GetRecognitionEventConnectionsChangedCallback()), + SpeechEndDetected(GetRecognitionEventConnectionsChangedCallback()), + Transcribing(GetRecoEventConnectionsChangedCallback()), + Transcribed(GetRecoEventConnectionsChangedCallback()), + Canceled(GetRecoCanceledEventConnectionsChangedCallback()), + m_hasyncStartContinuous(SPXHANDLE_INVALID), + m_hasyncStopContinuous(SPXHANDLE_INVALID), + m_properties(hreco), + Properties(m_properties) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + } + + /// + /// Destructor. + /// + ~ConversationTranscriber() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + TermRecognizer(); + } + + /// + /// Signal for events indicating the start of a recognition session (operation). + /// + EventSignal SessionStarted; + + /// + /// Signal for events indicating the end of a recognition session (operation). + /// + EventSignal SessionStopped; + + /// + /// Signal for events indicating the start of speech. + /// + EventSignal SpeechStartDetected; + + /// + /// Signal for events indicating the end of speech. + /// + EventSignal SpeechEndDetected; + + /// + /// Signal for events containing intermediate recognition results. + /// + EventSignal Transcribing; + + /// + /// Signal for events containing final recognition results. + /// (indicating a successful recognition attempt). + /// + EventSignal Transcribed; + + /// + /// Signal for events containing canceled recognition results + /// (indicating a recognition attempt that was canceled as a result or a direct cancellation request + /// or, alternatively, a transport or protocol failure). + /// + EventSignal Canceled; + + /// + /// Sets the authorization token that will be used for connecting the server. + /// + /// The authorization token. + void SetAuthorizationToken(const SPXSTRING& token) + { + Properties.SetProperty(PropertyId::SpeechServiceAuthorization_Token, token); + } + + /// + /// Gets the authorization token. + /// + /// Authorization token + SPXSTRING GetAuthorizationToken() + { + return Properties.GetProperty(PropertyId::SpeechServiceAuthorization_Token, SPXSTRING()); + } + + protected: + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Recognizer handle. + virtual void TermRecognizer() override + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + // Disconnect the event signals in reverse construction order + Canceled.DisconnectAll(); + Transcribed.DisconnectAll(); + Transcribing.DisconnectAll(); + SpeechEndDetected.DisconnectAll(); + SpeechStartDetected.DisconnectAll(); + SessionStopped.DisconnectAll(); + SessionStarted.DisconnectAll(); + + // Close the async handles we have open for Recognize, StartContinuous, and StopContinuous + for (auto handle : { &m_hasyncStartContinuous, &m_hasyncStopContinuous }) + { + if (*handle != SPXHANDLE_INVALID && ::recognizer_async_handle_is_valid(*handle)) + { + ::recognizer_async_handle_release(*handle); + *handle = SPXHANDLE_INVALID; + } + } + + // Ask the base to term + Recognizer::TermRecognizer(); + } + + void RecoEventConnectionsChanged(const EventSignal& recoEvent) + { + if (m_hreco != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_hreco=0x%8p", __FUNCTION__, (void*)m_hreco); + SPX_DBG_TRACE_VERBOSE_IF(!::recognizer_handle_is_valid(m_hreco), "%s: m_hreco is INVALID!!!", __FUNCTION__); + + if (&recoEvent == &Transcribing) + { + recognizer_recognizing_set_callback(m_hreco, Transcribing.IsConnected() ? FireEvent_Transcribing : nullptr, this); + } + else if (&recoEvent == &Transcribed) + { + recognizer_recognized_set_callback(m_hreco, Transcribed.IsConnected() ? FireEvent_Transcribed : nullptr, this); + } + } + } + + static void FireEvent_Transcribing(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr recoEvent{ new ConversationTranscriptionEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->Transcribing.Signal(*recoEvent.get()); + } + + static void FireEvent_Transcribed(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr recoEvent{ new ConversationTranscriptionEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->Transcribed.Signal(*recoEvent.get()); + } + + void RecoCanceledEventConnectionsChanged(const EventSignal& recoEvent) + { + if (m_hreco != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_hreco=0x%8p", __FUNCTION__, (void*)m_hreco); + SPX_DBG_TRACE_VERBOSE_IF(!::recognizer_handle_is_valid(m_hreco), "%s: m_hreco is INVALID!!!", __FUNCTION__); + + if (&recoEvent == &Canceled) + { + recognizer_canceled_set_callback(m_hreco, Canceled.IsConnected() ? FireEvent_Canceled : nullptr, this); + } + } + } + + static void FireEvent_Canceled(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + + auto ptr = new ConversationTranscriptionCanceledEventArgs(hevent); + std::shared_ptr recoEvent(ptr); + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->Canceled.Signal(*ptr); + } + + void SessionEventConnectionsChanged(const EventSignal& sessionEvent) + { + if (m_hreco != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_hreco=0x%8p", __FUNCTION__, (void*)m_hreco); + SPX_DBG_TRACE_VERBOSE_IF(!::recognizer_handle_is_valid(m_hreco), "%s: m_hreco is INVALID!!!", __FUNCTION__); + + if (&sessionEvent == &SessionStarted) + { + recognizer_session_started_set_callback(m_hreco, SessionStarted.IsConnected() ? FireEvent_SessionStarted : nullptr, this); + } + else if (&sessionEvent == &SessionStopped) + { + recognizer_session_stopped_set_callback(m_hreco, SessionStopped.IsConnected() ? FireEvent_SessionStopped : nullptr, this); + } + } + } + + static void FireEvent_SessionStarted(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr sessionEvent{ new SessionEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->SessionStarted.Signal(*sessionEvent.get()); + + // SessionEventArgs doesn't hold hevent, and thus can't release it properly ... release it here + SPX_DBG_ASSERT(recognizer_event_handle_is_valid(hevent)); + recognizer_event_handle_release(hevent); + } + + static void FireEvent_SessionStopped(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr sessionEvent{ new SessionEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->SessionStopped.Signal(*sessionEvent.get()); + + // SessionEventArgs doesn't hold hevent, and thus can't release it properly ... release it here + SPX_DBG_ASSERT(recognizer_event_handle_is_valid(hevent)); + recognizer_event_handle_release(hevent); + } + + void RecognitionEventConnectionsChanged(const EventSignal& recognitionEvent) + { + if (m_hreco != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_hreco=0x%8p", __FUNCTION__, (void*)m_hreco); + SPX_DBG_TRACE_VERBOSE_IF(!::recognizer_handle_is_valid(m_hreco), "%s: m_hreco is INVALID!!!", __FUNCTION__); + + if (&recognitionEvent == &SpeechStartDetected) + { + recognizer_speech_start_detected_set_callback(m_hreco, SpeechStartDetected.IsConnected() ? FireEvent_SpeechStartDetected : nullptr, this); + } + else if (&recognitionEvent == &SpeechEndDetected) + { + recognizer_speech_end_detected_set_callback(m_hreco, SpeechEndDetected.IsConnected() ? FireEvent_SpeechEndDetected : nullptr, this); + } + } + } + + static void FireEvent_SpeechStartDetected(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr recoEvent{ new RecognitionEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->SpeechStartDetected.Signal(*recoEvent.get()); + + // RecognitionEventArgs doesn't hold hevent, and thus can't release it properly ... release it here + SPX_DBG_ASSERT(recognizer_event_handle_is_valid(hevent)); + recognizer_event_handle_release(hevent); + } + + static void FireEvent_SpeechEndDetected(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr recoEvent{ new RecognitionEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->SpeechEndDetected.Signal(*recoEvent.get()); + + // RecognitionEventArgs doesn't hold hevent, and thus can't release it properly ... release it here + SPX_DBG_ASSERT(recognizer_event_handle_is_valid(hevent)); + recognizer_event_handle_release(hevent); + } + + /*! \endcond */ + +private: + + SPXASYNCHANDLE m_hasyncStartContinuous; + SPXASYNCHANDLE m_hasyncStopContinuous; + + DISABLE_DEFAULT_CTORS(ConversationTranscriber); + friend class Microsoft::CognitiveServices::Speech::Session; + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXRECOHANDLE hreco) : + PropertyCollection( + [=]() { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + recognizer_get_property_bag(hreco, &hpropbag); + return hpropbag; + }()) + { + } + }; + + PrivatePropertyCollection m_properties; + + inline std::function&)> GetSessionEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& sessionEvent) { this->SessionEventConnectionsChanged(sessionEvent); }; + } + + inline std::function&)> GetRecoEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& recoEvent) { this->RecoEventConnectionsChanged(recoEvent); }; + } + + inline std::function&)> GetRecoCanceledEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& recoEvent) { this->RecoCanceledEventConnectionsChanged(recoEvent); }; + } + + inline std::function&)> GetRecognitionEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& recoEvent) { this->RecognitionEventConnectionsChanged(recoEvent); }; + } + +public: + /// + /// A collection of properties and their values defined for this . + /// + PropertyCollection& Properties; +}; + + +} } } } // Microsoft::CognitiveServices::Speech::Transcription diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_transcription_eventargs.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_transcription_eventargs.h new file mode 100644 index 0000000..dd03343 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_transcription_eventargs.h @@ -0,0 +1,165 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_conversation_transcription_eventargs.h: Public API declarations for ConversationTranscriptionEventArgs C++ class +// + +#pragma once +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Transcription { + +/// +/// Class for conversation transcriber event arguments. +/// +class ConversationTranscriptionEventArgs : public RecognitionEventArgs +{ +private: + + SPXEVENTHANDLE m_hevent; + std::shared_ptr m_result; + +public: + + /// + /// Constructor. + /// + /// Event handle + explicit ConversationTranscriptionEventArgs(SPXEVENTHANDLE hevent) : + RecognitionEventArgs(hevent), + m_hevent(hevent), + m_result(std::make_shared(ResultHandleFromEventHandle(hevent))), + Result(m_result) + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hevent); + }; + + /// + virtual ~ConversationTranscriptionEventArgs() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hevent); + SPX_THROW_ON_FAIL(recognizer_event_handle_release(m_hevent)); + }; + +#if defined(BINDING_OBJECTIVE_C) +private: +#endif + /// + /// Conversation transcriber result. + /// + std::shared_ptr Result; + +#if defined(BINDING_OBJECTIVE_C) +public: +#else +protected: +#endif + + /*! \cond PROTECTED */ + + /// + /// Conversation transcriber result. + /// + std::shared_ptr GetResult() const { return m_result; } + + /*! \endcond */ + +private: + + DISABLE_DEFAULT_CTORS(ConversationTranscriptionEventArgs); + + SPXRESULTHANDLE ResultHandleFromEventHandle(SPXEVENTHANDLE hevent) + { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(recognizer_recognition_event_get_result(hevent, &hresult)); + return hresult; + } +}; + + +/// +/// Class for conversation transcriber canceled event arguments. +/// +class ConversationTranscriptionCanceledEventArgs : public ConversationTranscriptionEventArgs +{ +private: + + std::shared_ptr m_cancellation; + CancellationReason m_cancellationReason; + CancellationErrorCode m_errorCode; + +public: + + /// + /// Constructor. + /// + /// Event handle + explicit ConversationTranscriptionCanceledEventArgs(SPXEVENTHANDLE hevent) : + ConversationTranscriptionEventArgs(hevent), + m_cancellation(CancellationDetails::FromResult(GetResult())), + m_cancellationReason(m_cancellation->Reason), + m_errorCode(m_cancellation->ErrorCode), + Reason(m_cancellationReason), + ErrorCode(m_errorCode), + ErrorDetails(m_cancellation->ErrorDetails) + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p)", __FUNCTION__, (void*)this); + }; + + /// + virtual ~ConversationTranscriptionCanceledEventArgs() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p)", __FUNCTION__, (void*)this); + }; + +#if defined(BINDING_OBJECTIVE_C) +private: +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif + /// + /// The reason the result was canceled. + /// + const CancellationReason& Reason; + + /// + /// The error code in case of an unsuccessful recognition ( is set to Error). + /// If Reason is not Error, ErrorCode is set to NoError. + /// + const CancellationErrorCode& ErrorCode; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + /// + /// The error message in case of an unsuccessful recognition ( is set to Error). + /// + const SPXSTRING ErrorDetails; + +#if defined(BINDING_OBJECTIVE_C) +public: +#else +private: +#endif + /// + /// CancellationDetails. + /// + std::shared_ptr GetCancellationDetails() const { return m_cancellation; } + +private: + + DISABLE_DEFAULT_CTORS(ConversationTranscriptionCanceledEventArgs); +}; +} } } } // Microsoft::CognitiveServices::Speech::Transcription diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_transcription_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_transcription_result.h new file mode 100644 index 0000000..c655c8f --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_transcription_result.h @@ -0,0 +1,72 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_conversation_transcription_result.h: Public API declarations for ConversationTranscription C++ class +// + +#pragma once +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Transcription { + +/// +/// Represents the result of a conversation transcriber. +/// +class ConversationTranscriptionResult final : public RecognitionResult +{ +public: + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Result handle. + explicit ConversationTranscriptionResult(SPXRESULTHANDLE hresult) : + RecognitionResult(hresult), + SpeakerId(m_speakerId) + { + PopulateSpeakerFields(hresult, &m_speakerId); + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p) -- resultid=%s; reason=0x%x; text=%s, speakerid=%s, utteranceid=%s", __FUNCTION__, (void*)this, (void*)Handle, Utils::ToUTF8(ResultId).c_str(), Reason, Utils::ToUTF8(Text).c_str(), Utils::ToUTF8(SpeakerId).c_str()); + } + + /// + /// Destructor. + /// + ~ConversationTranscriptionResult() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)Handle); + } + + /// + /// Unique Speaker id. + /// + const SPXSTRING& SpeakerId; + +private: + DISABLE_DEFAULT_CTORS(ConversationTranscriptionResult); + + void PopulateSpeakerFields(SPXRESULTHANDLE hresult, SPXSTRING* pspeakerId) + { + SPX_INIT_HR(hr); + + const size_t maxCharCount = 1024; + char sz[maxCharCount + 1] = {}; + + if (pspeakerId != nullptr && recognizer_result_handle_is_valid(hresult)) + { + SPX_THROW_ON_FAIL(hr = conversation_transcription_result_get_speaker_id(hresult, sz, maxCharCount)); + *pspeakerId = Utils::ToSPXString(sz); + } + } + + SPXSTRING m_speakerId; +}; + +} } } } // Microsoft::CognitiveServices::Speech::Transcription diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_translator.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_translator.h new file mode 100644 index 0000000..d23b53b --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_translator.h @@ -0,0 +1,448 @@ + +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_conversation_translator.h: Public API declarations for ConversationTranslator C++ class +// + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +// Forward decl: facilitates friend use of Connection::FromConversationTranslator +class Connection; + +namespace Transcription { + + /// + /// A conversation translator that enables a connected experience where participants can use their + /// own devices to see everyone else's recognitions and IMs in their own languages. Participants + /// can also speak and send IMs to others. + /// Added in 1.9.0 + /// + class ConversationTranslator : public std::enable_shared_from_this + { + private: + /*! \cond PRIVATE */ + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXCONVERSATIONHANDLE hconvtrans) : + PropertyCollection([hconvtrans]() + { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + conversation_translator_get_property_bag(hconvtrans, &hpropbag); + return hpropbag; + }()) + {} + }; + + SPXCONVERSATIONTRANSLATORHANDLE m_handle; + PrivatePropertyCollection m_properties; + /*! \endcond */ + + public: + /// + /// Creates a conversation translator from an audio config + /// + /// Audio configuration. + /// Smart pointer to conversation translator instance. + static std::shared_ptr FromConfig(std::shared_ptr audioConfig = nullptr) + { + SPXCONVERSATIONTRANSLATORHANDLE handle; + SPX_THROW_ON_FAIL(::conversation_translator_create_from_config( + &handle, + Utils::HandleOrInvalid(audioConfig) + )); + return std::shared_ptr(new ConversationTranslator(handle)); + } + + /// + /// Destructor + /// + virtual ~ConversationTranslator() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + // disconnect callbacks in reverse order + TextMessageReceived.DisconnectAll(); + Transcribed.DisconnectAll(); + Transcribing.DisconnectAll(); + ConversationExpiration.DisconnectAll(); + ParticipantsChanged.DisconnectAll(); + Canceled.DisconnectAll(); + SessionStopped.DisconnectAll(); + SessionStarted.DisconnectAll(); + + ::conversation_translator_handle_release(m_handle); + m_handle = SPXHANDLE_INVALID; + } + + /// + /// Signal for events indicating the start of a transcription session (operation). + /// + EventSignal SessionStarted; + + /// + /// Signal for events indicating the end of a transcription session (operation). + /// + EventSignal SessionStopped; + + /// + /// Signal for events containing canceled recognition results + /// (indicating a recognition attempt that was canceled as a result or a direct cancellation request + /// or, alternatively, a transport or protocol failure). + /// + EventSignal Canceled; + + /// + /// Signal for events indicating the conversation participants have changed. + /// + EventSignal ParticipantsChanged; + + /// + /// Signal for event indicating how many minutes are left until a conversation expires. + /// + EventSignal ConversationExpiration; + + /// + /// Signal for events containing intermediate translated conversation transcription results. + /// + EventSignal Transcribing; + + /// + /// Signal for events containing final translated conversation transcription results. + /// (indicating a successful recognition attempt). + /// + EventSignal Transcribed; + + /// + /// Raised when a text message is received from the conversation. + /// + EventSignal TextMessageReceived; + + /// + /// Joins a conversation. After you call this, you will start receiving events. + /// + /// The conversation instance to use. This instance can be used by the + /// host to manage the conversation. + /// The display name to use for the current participant in the conversation. + /// An asynchronous operation. + std::future JoinConversationAsync(std::shared_ptr conversation, const SPXSTRING& nickname) + { + return RunAsync([conversation, nickname](auto handle) + { + return ::conversation_translator_join( + handle, + Utils::HandleOrInvalid(conversation), + Utils::ToUTF8(nickname).c_str()); + }); + } + + /// + /// Joins a conversation. After you call this, you will start receiving events. + /// + /// The identifier of the conversation you want to join. + /// The display name of the current participant in the conversation. + /// The language the participant is using. + /// An asynchronous operation. + std::future JoinConversationAsync(const SPXSTRING& conversationId, const SPXSTRING& nickname, const SPXSTRING& language) + { + return RunAsync([conversationId, nickname, language](auto handle) + { + return ::conversation_translator_join_with_id( + handle, + Utils::ToUTF8(conversationId).c_str(), + Utils::ToUTF8(nickname).c_str(), + Utils::ToUTF8(language).c_str()); + }); + } + + /// + /// Starts sending audio to the conversation service for speech recognition. + /// + /// An asynchronous operation. + std::future StartTranscribingAsync() + { + return RunAsync(::conversation_translator_start_transcribing); + } + + /// + /// Stops sending audio to the conversation service. + /// + /// An asynchronous operation. + std::future StopTranscribingAsync() + { + return RunAsync(::conversation_translator_stop_transcribing); + } + + /// + /// Sends an instant message to all participants in the conversation. This instant message + /// will be translated into each participant's text language. + /// + /// The message to send. + /// An asynchronous operation. + std::future SendTextMessageAsync(const SPXSTRING& message) + { + return RunAsync([message](auto handle) + { + return ::conversation_translator_send_text_message( + handle, + Utils::ToUTF8(message).c_str()); + }); + } + + /// + /// Leaves the current conversation. After this is called, you will no longer receive any events. + /// + /// An asynchronous operation. + std::future LeaveConversationAsync() + { + return RunAsync(::conversation_translator_leave); + } + + /// + /// Sets the Cognitive Speech authorization token that will be used for connecting to the server. + /// + /// The authorization token. + /// The Azure region for this token. + void SetAuthorizationToken(const SPXSTRING& authToken, const SPXSTRING& region) + { + SPX_THROW_ON_FAIL(::conversation_translator_set_authorization_token( + m_handle, + Utils::ToUTF8(authToken).c_str(), + Utils::ToUTF8(region).c_str())); + } + + /// + /// Gets the authorization token. + /// + /// Authorization token + SPXSTRING GetAuthorizationToken() + { + return m_properties.GetProperty(PropertyId::SpeechServiceAuthorization_Token); + } + + /// + /// Gets your participant identifier + /// + /// Participant ID + SPXSTRING GetParticipantId() + { + return m_properties.GetProperty(PropertyId::Conversation_ParticipantId); + } + + /// + /// A collection of properties and their values defined for this . + /// + PropertyCollection& Properties; + + protected: + explicit ConversationTranslator(SPXCONVERSATIONTRANSLATORHANDLE handle) : + m_handle(handle), + m_properties(handle), + SessionStarted(BindHandler(&ConversationTranslator::OnSessionEventChanged)), + SessionStopped(BindHandler(&ConversationTranslator::OnSessionEventChanged)), + Canceled(BindHandler(&ConversationTranslator::OnCanceledEventChanged)), + ParticipantsChanged(BindHandler(&ConversationTranslator::OnParticipantsEventChanged)), + ConversationExpiration(BindHandler(&ConversationTranslator::OnExpirationEventChanged)), + Transcribing(BindHandler(&ConversationTranslator::OnTranscriptionEventChanged)), + Transcribed(BindHandler(&ConversationTranslator::OnTranscriptionEventChanged)), + TextMessageReceived(BindHandler(&ConversationTranslator::OnTextMessageEventChanged)), + Properties(m_properties) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + } + + static inline bool ValidateHandle(SPXCONVERSATIONTRANSLATORHANDLE handle, const char* function) + { + UNUSED(function); // not used in release builds + SPX_DBG_TRACE_VERBOSE("%s: handle=0x%8p", function, (void*)handle); + bool valid = ::conversation_translator_handle_is_valid(handle); + SPX_DBG_TRACE_VERBOSE_IF(!valid, "%s: handle is INVALID!!!", function); + return valid; + } + + void OnSessionEventChanged(const EventSignal& evt) + { + if (!ValidateHandle(m_handle, __FUNCTION__)) return; + + PCONV_TRANS_CALLBACK callback = nullptr; + + if (&evt == &SessionStarted) + { + if (SessionStarted.IsConnected()) + { + callback = [](auto, auto b, auto c) { FireEvent(b, c, &ConversationTranslator::SessionStarted); }; + } + + conversation_translator_session_started_set_callback(m_handle, callback, this); + } + else if (&evt == &SessionStopped) + { + if (SessionStopped.IsConnected()) + { + callback = [](auto, auto b, auto c) { FireEvent(b, c, &ConversationTranslator::SessionStopped); }; + } + + conversation_translator_session_stopped_set_callback(m_handle, callback, this); + } + } + + void OnCanceledEventChanged(const EventSignal&) + { + if (!ValidateHandle(m_handle, __FUNCTION__)) return; + + PCONV_TRANS_CALLBACK callback = nullptr; + if (Canceled.IsConnected()) + { + callback = [](auto, auto b, auto c) { FireEvent(b, c, &ConversationTranslator::Canceled); }; + } + + conversation_translator_canceled_set_callback(m_handle, callback, this); + } + + void OnParticipantsEventChanged(const EventSignal&) + { + if (!ValidateHandle(m_handle, __FUNCTION__)) return; + + PCONV_TRANS_CALLBACK callback = nullptr; + if (ParticipantsChanged.IsConnected()) + { + callback = [](auto, auto b, auto c) { FireEvent(b, c, &ConversationTranslator::ParticipantsChanged); }; + } + + conversation_translator_participants_changed_set_callback(m_handle, callback, this); + } + + void OnExpirationEventChanged(const EventSignal&) + { + if (!ValidateHandle(m_handle, __FUNCTION__)) return; + + PCONV_TRANS_CALLBACK callback = nullptr; + if (ConversationExpiration.IsConnected()) + { + callback = [](auto, auto b, auto c) { FireEvent(b, c, &ConversationTranslator::ConversationExpiration); }; + } + + conversation_translator_conversation_expiration_set_callback(m_handle, callback, this); + } + + void OnTranscriptionEventChanged(const EventSignal& evt) + { + if (!ValidateHandle(m_handle, __FUNCTION__)) return; + + PCONV_TRANS_CALLBACK callback = nullptr; + if (&evt == &Transcribing) + { + if (Transcribing.IsConnected()) + { + callback = [](auto, auto b, auto c) { FireEvent(b, c, &ConversationTranslator::Transcribing); }; + } + + conversation_translator_transcribing_set_callback(m_handle, callback, this); + } + else + { + if (Transcribed.IsConnected()) + { + callback = [](auto, auto b, auto c) { FireEvent(b, c, &ConversationTranslator::Transcribed); }; + } + + conversation_translator_transcribed_set_callback(m_handle, callback, this); + } + } + + void OnTextMessageEventChanged(const EventSignal&) + { + if (!ValidateHandle(m_handle, __FUNCTION__)) return; + + PCONV_TRANS_CALLBACK callback = nullptr; + if (TextMessageReceived.IsConnected()) + { + callback = [](auto, auto b, auto c) { FireEvent(b, c, &ConversationTranslator::TextMessageReceived); }; + } + + conversation_translator_text_message_recevied_set_callback(m_handle, callback, this); + } + + private: + /*! \cond PRIVATE */ + + friend class Microsoft::CognitiveServices::Speech::Connection; + + DISABLE_DEFAULT_CTORS(ConversationTranslator); + + inline std::future RunAsync(std::function func) + { + auto keepalive = this->shared_from_this(); + return std::async(std::launch::async, [keepalive, this, func]() + { + SPX_THROW_ON_FAIL(func(m_handle)); + }); + } + + template + inline std::function BindHandler(void (ConversationTranslator::*func)(TArg)) + { + return [this, func](TArg arg) + { + (this->*func)(arg); + }; + } + + static inline void FreeEventHandle(SPXEVENTHANDLE hEvt) + { + if (::conversation_translator_event_handle_is_valid(hEvt)) + { + ::conversation_translator_event_handle_release(hEvt); + } + } + + template + static inline void FireEvent(SPXEVENTHANDLE hEvt, void* pCtxt, EventSignal ConversationTranslator::*pEvent) + { + try + { + auto pThis = static_cast(pCtxt); + SPX_DBG_ASSERT(pThis != nullptr); + auto keepAlive = pThis->shared_from_this(); + + T eventArgs(hEvt); + (pThis->*pEvent).Signal(eventArgs); + + // event classes don't properly release the handles so do that here + FreeEventHandle(hEvt); + } + catch (std::exception& ex) + { + UNUSED(ex); + FreeEventHandle(hEvt); + throw; + } + catch (...) + { + FreeEventHandle(hEvt); + throw; + } + } + + /*! \endcond */ + }; + +}}}} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_translator_events.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_translator_events.h new file mode 100644 index 0000000..0bc817c --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversation_translator_events.h @@ -0,0 +1,262 @@ + +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_conversation_translator_events.h: Public C++ class API declarations for ConversationTranslator related events +// + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Transcription { + + /// + /// Helper class with additional methods + /// Added in 1.9.0 + /// + class EventHelper + { + protected: + template + static TVal GetValue(THandle hevent, SPXHR(SPXAPI_CALLTYPE * func)(THandle hevent, TVal* ptr)) + { + TVal value; + SPX_THROW_ON_FAIL(func(hevent, &value)); + return value; + } + + template + static SPXSTRING GetStringValue(THandle hevent, SPXHR(SPXAPI_CALLTYPE * func)(THandle hevent, char * psz, uint32_t cch)) + { + const uint32_t maxCharCount = 1024; + char sz[maxCharCount + 1]; + SPX_THROW_ON_FAIL(func(hevent, sz, maxCharCount)); + return Utils::ToSPXString(sz); + } + + template + static SPXSTRING GetStringValue(THandle hevent, SPXHR(SPXAPI_CALLTYPE* func)(THandle hevent, char* psz, uint32_t* pcch)) + { + // query the string length + uint32_t length = 0; + SPX_THROW_ON_FAIL(func(hevent, nullptr, &length)); + + // retrieve the string + std::unique_ptr buffer(new char[length]); + SPX_THROW_ON_FAIL(func(hevent, buffer.get(), &length)); + return Utils::ToSPXString(buffer.get()); + } + }; + + /// + /// Represents the result of a conversation translator recognition, or text message. + /// Added in 1.9.0 + /// + class ConversationTranslationResult : public Translation::TranslationRecognitionResult, public EventHelper + { + private: + SPXSTRING m_participantId; + SPXSTRING m_originalLang; + + public: + explicit ConversationTranslationResult(SPXRESULTHANDLE resultHandle) : + Translation::TranslationRecognitionResult(resultHandle), + m_participantId(GetStringValue(resultHandle, conversation_translator_result_get_user_id)), + m_originalLang(GetStringValue(resultHandle, conversation_translator_result_get_original_lang)), + ParticipantId(m_participantId), + OriginalLanguage(m_originalLang) + { + } + + /// + /// The unique participant identifier + /// + const SPXSTRING& ParticipantId; + + /// + /// Gets the language that the original recognition or text message is in + /// + const SPXSTRING& OriginalLanguage; + + private: + DISABLE_COPY_AND_MOVE(ConversationTranslationResult); + }; + + /// + /// Event arguments for the ConversationExpiration event. + /// Added in 1.9.0 + /// + class ConversationExpirationEventArgs : public SessionEventArgs, public EventHelper + { + private: + std::chrono::minutes m_expirationTime; + + public: + /// + /// Creates a new instance. + /// + /// The event handle. + explicit ConversationExpirationEventArgs(SPXEVENTHANDLE hevent) : + SessionEventArgs(hevent), + m_expirationTime(std::chrono::minutes(GetValue(hevent, conversation_translator_event_get_expiration_time))), + ExpirationTime(m_expirationTime) + { + } + + /// + /// How many minutes are left until the conversation expires + /// + const std::chrono::minutes& ExpirationTime; + + private: + DISABLE_COPY_AND_MOVE(ConversationExpirationEventArgs); + }; + + /// + /// Event arguments for the ParticipantsChanged event. + /// Added in 1.9.0 + /// + class ConversationParticipantsChangedEventArgs : public SessionEventArgs, public EventHelper + { + private: + ParticipantChangedReason m_reason; + std::vector> m_participants; + + public: + /// + /// Creates a new instance. + /// + /// The event handle. + explicit ConversationParticipantsChangedEventArgs(SPXEVENTHANDLE hevent) : + SessionEventArgs(hevent), + m_reason(GetValue(hevent, conversation_translator_event_get_participant_changed_reason)), + m_participants(GetParticipants(hevent)), + Reason(m_reason), + Participants(m_participants) + { + } + + /// + /// Why the participant changed event was raised (e.g. a participant joined) + /// + const ParticipantChangedReason& Reason; + + /// + /// The participant(s) that joined, left, or were updated + /// + const std::vector>& Participants; + + protected: + /*! \cond PROTECTED */ + + std::vector> GetParticipants(SPXEVENTHANDLE hevent) + { + std::vector> list; + + SPXPARTICIPANTHANDLE hparticipant = nullptr; + for (int i = 0; hparticipant != SPXHANDLE_INVALID; i++) + { + SPX_THROW_ON_FAIL(conversation_translator_event_get_participant_changed_at_index(hevent, i, &hparticipant)); + if (hparticipant != SPXHANDLE_INVALID) + { + list.push_back(std::make_shared(hparticipant)); + + // the Participant object correctly frees the handle so we don't need to do anything + // special here + } + } + + return list; + } + + /*! \endcond */ + + private: + DISABLE_COPY_AND_MOVE(ConversationParticipantsChangedEventArgs); + }; + + /// + /// Event arguments for the ConversationTranslator , + /// , or + /// events. + /// Added in 1.9.0 + /// + class ConversationTranslationEventArgs : public RecognitionEventArgs, public EventHelper + { + private: + std::shared_ptr m_result; + + public: + /// + /// Creates a new instance. + /// + /// The event handle returned by the C-API. + explicit ConversationTranslationEventArgs(SPXEVENTHANDLE hevent) + : RecognitionEventArgs(hevent), + m_result(std::make_shared(GetValue(hevent, recognizer_recognition_event_get_result))), + Result(m_result) + { + } + +#if defined(BINDING_OBJECTIVE_C) + private: +#endif + /// + /// Contains the conversation translation result. This could be for a canceled event, + /// a speech recognition, or a received text message. + /// + std::shared_ptr Result; + +#if defined(BINDING_OBJECTIVE_C) + public: +#else + protected: +#endif + + /*! \cond PROTECTED */ + + /// + /// Contains the conversation translation result. This could be for a canceled event, + /// a speech recognition, or a received text message. + /// + std::shared_ptr GetResult() const { return m_result; } + + /*! \endcond */ + + private: + DISABLE_COPY_AND_MOVE(ConversationTranslationEventArgs); + }; + + + /// + /// Event arguments for the conversation translator canceled event. + /// Added in 1.9.0 + /// + class ConversationTranslationCanceledEventArgs : public ConversationTranscriptionCanceledEventArgs + { + public: + /// + /// Creates a new instance. + /// + /// The event handle. + explicit ConversationTranslationCanceledEventArgs(SPXEVENTHANDLE hevent) : + ConversationTranscriptionCanceledEventArgs(hevent) + { } + }; + +}}}} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversational_language_understanding_model.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversational_language_understanding_model.h new file mode 100644 index 0000000..1a2eb54 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_conversational_language_understanding_model.h @@ -0,0 +1,89 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license201809 for the full license information. +// +// speechapi_cxx_conversational_language_understanding_model.h: Public API declarations for PatternMatchingModel C++ class +// + +#pragma once +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Intent { + + /// + /// Represents a Conversational Language Understanding used for intent recognition. + /// + class ConversationalLanguageUnderstandingModel : public LanguageUnderstandingModel + { + public: + + /// + /// Creates a Conversational Language Understanding (CLU) model using the specified model ID. + /// + /// The Azure Language resource key. + /// The Azure Language resource endpoint. + /// The Conversational Language Understanding project name. + /// The Conversational Language Understanding deployment name. + /// A shared pointer to the Conversational Language Understanding model. + static std::shared_ptr FromResource(const SPXSTRING& languageResourceKey, const SPXSTRING& endpoint, const SPXSTRING& projectName, const SPXSTRING& deploymentName) + { + return std::shared_ptr { + new ConversationalLanguageUnderstandingModel(languageResourceKey, endpoint, projectName, deploymentName) + }; + } + + /// + /// Returns id for this model. Defaults to projectName-deploymentName. + /// + /// A string representing the id of this model. + SPXSTRING GetModelId() const { return m_modelId; } + + /// + /// Sets the id for this model. Defaults to projectName-deploymentName. + /// + /// A string representing the id of this model. + void SetModelId(SPXSTRING value) { m_modelId = value; } + + /// + /// This is the Azure language resource key to be used with this model. + /// + SPXSTRING languageResourceKey; + + /// + /// Conversational Language Understanding deployment endpoint to contact. + /// + SPXSTRING endpoint; + + /// + /// Conversational Language Understanding project name. + /// + SPXSTRING projectName; + + /// + /// Conversational Language Understanding deployment name. + /// + SPXSTRING deploymentName; + + private: + DISABLE_COPY_AND_MOVE(ConversationalLanguageUnderstandingModel); + + ConversationalLanguageUnderstandingModel(const SPXSTRING& languageResourceKey, const SPXSTRING& endpoint, const SPXSTRING& projectName, const SPXSTRING& deploymentName) : + LanguageUnderstandingModel(LanguageUnderstandingModelType::ConversationalLanguageUnderstandingModel), + languageResourceKey(languageResourceKey), + endpoint(endpoint), + projectName(projectName), + deploymentName(deploymentName) + { + m_modelId = projectName + "-" + deploymentName; + } + + SPXSTRING m_modelId; +}; + +} } } } // Microsoft::CognitiveServices::Speech::Intent diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_dialog_service_config.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_dialog_service_config.h new file mode 100644 index 0000000..641daaf --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_dialog_service_config.h @@ -0,0 +1,268 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +#pragma once + +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Dialog { + +/// +/// Class that defines base configurations for the dialog service connector object. +/// +class DialogServiceConfig +{ +protected: + /*! \cond PROTECTED */ + inline explicit DialogServiceConfig(SPXSPEECHCONFIGHANDLE h_config) : m_config{ h_config } + { + } + SpeechConfig m_config; + /*! \endcond */ + +public: + /// + /// Default destructor. + /// + virtual ~DialogServiceConfig() = default; + + /// + /// Internal operator used to get underlying handle value. + /// + /// A handle. + explicit operator SPXSPEECHCONFIGHANDLE() const { return static_cast(m_config); } + + /// + /// Sets a property value by name. + /// + /// The property name. + /// The property value. + void SetProperty(const SPXSTRING& name, const SPXSTRING& value) + { + m_config.SetProperty(name, value); + } + + /// + /// Sets a property value by ID. + /// + /// The property id. + /// The property value. + void SetProperty(PropertyId id, const SPXSTRING& value) + { + m_config.SetProperty(id, value); + } + + /// + /// Gets a property value by name. + /// + /// The parameter name. + /// The property value. + SPXSTRING GetProperty(const SPXSTRING& name) const + { + return m_config.GetProperty(name); + } + + /// + /// Gets a property value by ID. + /// + /// The parameter id. + /// The property value. + SPXSTRING GetProperty(PropertyId id) const + { + return m_config.GetProperty(id); + } + + /// + /// Sets a property value that will be passed to service using the specified channel. + /// + /// The property name. + /// The property value. + /// The channel used to pass the specified property to service. + void SetServiceProperty(const SPXSTRING& name, const SPXSTRING& value, ServicePropertyChannel channel) + { + m_config.SetServiceProperty(name, value, channel); + } + + + /// + /// Sets proxy configuration + /// + /// Note: Proxy functionality is not available on macOS. This function will have no effect on this platform. + /// + /// The host name of the proxy server, without the protocol scheme (`http://`) + /// The port number of the proxy server + /// The user name of the proxy server + /// The password of the proxy server + void SetProxy(const SPXSTRING& proxyHostName, uint32_t proxyPort, const SPXSTRING& proxyUserName = SPXSTRING(), const SPXSTRING& proxyPassword = SPXSTRING()) + { + m_config.SetProxy(proxyHostName, proxyPort, proxyUserName, proxyPassword); + } + + /// + /// Set the input language to the connector. + /// + /// Specifies the name of spoken language to be recognized in BCP-47 format. + void SetLanguage(const SPXSTRING& lang) + { + SetProperty(PropertyId::SpeechServiceConnection_RecoLanguage, lang); + } + + /// + /// Gets the input language to the connector. + /// The language is specified in BCP-47 format. + /// + /// The connetor language. + SPXSTRING GetLanguage() const + { + return GetProperty(PropertyId::SpeechServiceConnection_RecoLanguage); + } + +}; + +/// +/// Class that defines configurations for the dialog service connector object for using a Bot Framework backend. +/// +class BotFrameworkConfig final : public DialogServiceConfig +{ +public: + /// + /// Creates a bot framework service config instance with the specified subscription key and region. + /// + /// Subscription key associated with the bot + /// The region name (see the region page). + /// A shared pointer to the new bot framework config. + inline static std::shared_ptr FromSubscription(const SPXSTRING& subscription, const SPXSTRING& region) + { + SPXSPEECHCONFIGHANDLE h_config = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(bot_framework_config_from_subscription(&h_config, Utils::ToUTF8(subscription).c_str(), Utils::ToUTF8(region).c_str(), nullptr)); + return std::shared_ptr{ new BotFrameworkConfig(h_config) }; + } + + /// + /// Creates a bot framework service config instance with the specified subscription key and region. + /// + /// Subscription key associated with the bot + /// The region name (see the region page). + /// Identifier used to select a bot associated with this subscription. + /// A shared pointer to the new bot framework config. + inline static std::shared_ptr FromSubscription(const SPXSTRING& subscription, const SPXSTRING& region, const SPXSTRING& bot_Id) + { + SPXSPEECHCONFIGHANDLE h_config = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(bot_framework_config_from_subscription(&h_config, Utils::ToUTF8(subscription).c_str(), Utils::ToUTF8(region).c_str(), Utils::ToUTF8(bot_Id).c_str())); + return std::shared_ptr{ new BotFrameworkConfig(h_config) }; + } + + /// + /// Creates a bot framework service config instance with the specified authorization token and region. + /// Note: The caller needs to ensure that the authorization token is valid. Before the authorization token + /// expires, the caller needs to refresh it by calling this setter with a new valid token. + /// As configuration values are copied when creating a new connector, the new token value will not apply to connectors that have already been created. + /// For connectors that have been created before, you need to set authorization token of the corresponding connector + /// to refresh the token. Otherwise, the connectors will encounter errors during operation. + /// + /// The authorization token. + /// The region name (see the region page). + /// A shared pointer to the new bot framework config. + inline static std::shared_ptr FromAuthorizationToken(const SPXSTRING& authToken, const SPXSTRING& region) + { + SPXSPEECHCONFIGHANDLE h_config = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(bot_framework_config_from_authorization_token(&h_config, Utils::ToUTF8(authToken).c_str(), Utils::ToUTF8(region).c_str(), nullptr)); + return std::shared_ptr{ new BotFrameworkConfig(h_config) }; + } + + /// + /// Creates a bot framework service config instance with the specified authorization token and region. + /// Note: The caller needs to ensure that the authorization token is valid. Before the authorization token + /// expires, the caller needs to refresh it by calling this setter with a new valid token. + /// As configuration values are copied when creating a new connector, the new token value will not apply to connectors that have already been created. + /// For connectors that have been created before, you need to set authorization token of the corresponding connector + /// to refresh the token. Otherwise, the connectors will encounter errors during operation. + /// + /// The authorization token. + /// The region name (see the region page). + /// Identifier used to select a bot associated with this subscription. + /// A shared pointer to the new bot framework config. + inline static std::shared_ptr FromAuthorizationToken(const SPXSTRING& authToken, const SPXSTRING& region, const SPXSTRING& bot_Id) + { + SPXSPEECHCONFIGHANDLE h_config = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(bot_framework_config_from_authorization_token(&h_config, Utils::ToUTF8(authToken).c_str(), Utils::ToUTF8(region).c_str(), Utils::ToUTF8(bot_Id).c_str())); + return std::shared_ptr{ new BotFrameworkConfig(h_config) }; + } +private: + inline explicit BotFrameworkConfig(SPXSPEECHCONFIGHANDLE h_config): DialogServiceConfig{ h_config } + { + } +}; + +/// +/// Class that defines configurations for the dialog service connector object for using a CustomCommands backend. +/// +class CustomCommandsConfig: public DialogServiceConfig +{ +public: + /// + /// Creates a Custom Commands config instance with the specified application id, subscription key and region. + /// + /// Custom Commands application id. + /// Subscription key associated with the bot + /// The region name (see the region page). + /// A shared pointer to the new bot framework config. + inline static std::shared_ptr FromSubscription(const SPXSTRING& appId, const SPXSTRING& subscription, const SPXSTRING& region) + { + SPXSPEECHCONFIGHANDLE h_config = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(custom_commands_config_from_subscription(&h_config, Utils::ToUTF8(appId).c_str(), Utils::ToUTF8(subscription).c_str(), Utils::ToUTF8(region).c_str())); + return std::shared_ptr{ new CustomCommandsConfig(h_config) }; + } + + /// + /// Creates a Custom Commands config instance with the specified application id authorization token and region. + /// Note: The caller needs to ensure that the authorization token is valid. Before the authorization token + /// expires, the caller needs to refresh it by calling this setter with a new valid token. + /// As configuration values are copied when creating a new connector, the new token value will not apply to connectors that have already been created. + /// For connectors that have been created before, you need to set authorization token of the corresponding connector + /// to refresh the token. Otherwise, the connectors will encounter errors during operation. + /// + /// Custom Commands application id. + /// The authorization token. + /// The region name (see the region page). + /// A shared pointer to the new bot framework config. + inline static std::shared_ptr FromAuthorizationToken(const SPXSTRING& appId, const SPXSTRING& authToken, const SPXSTRING& region) + { + SPXSPEECHCONFIGHANDLE h_config = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(custom_commands_config_from_authorization_token(&h_config, Utils::ToUTF8(appId).c_str(), Utils::ToUTF8(authToken).c_str(), Utils::ToUTF8(region).c_str())); + return std::shared_ptr{ new CustomCommandsConfig(h_config) }; + } + + /// + /// Sets the corresponding backend application identifier. + /// + /// Application identifier. + inline void SetApplicationId(const SPXSTRING& applicationId) + { + SetProperty(PropertyId::Conversation_ApplicationId, applicationId); + } + + /// + /// Gets the application identifier. + /// + /// Speech Channel Secret Key. + inline SPXSTRING GetApplicationId() const + { + return GetProperty(PropertyId::Conversation_ApplicationId); + } + +private: + inline explicit CustomCommandsConfig(SPXSPEECHCONFIGHANDLE h_config): DialogServiceConfig{ h_config } + { + } +}; + +} } } } diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_dialog_service_connector.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_dialog_service_connector.h new file mode 100644 index 0000000..5c77c2c --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_dialog_service_connector.h @@ -0,0 +1,547 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_dialog_service_connector.h: Public API declarations for DialogServiceConnector C++ base class +// + +#pragma once +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +// Forward decl: facilities friend use use of Connection::FromDialogServiceConnector +class Connection; + +namespace Dialog { + +/// +/// Object used to connect DirectLineSpeech or CustomCommands. +/// +/// +/// Objects of this type are created via the factory method. +/// +class DialogServiceConnector : public std::enable_shared_from_this, public Utils::NonCopyable, public Utils::NonMovable +{ +public: + /// + /// Destroys the instance. + /// + virtual ~DialogServiceConnector() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + // Disconnect the event signals in reverse construction order + TurnStatusReceived.DisconnectAll(); + ActivityReceived.DisconnectAll(); + Canceled.DisconnectAll(); + SpeechEndDetected.DisconnectAll(); + SpeechStartDetected.DisconnectAll(); + SessionStopped.DisconnectAll(); + SessionStarted.DisconnectAll(); + Recognizing.DisconnectAll(); + Recognized.DisconnectAll(); + + if (m_handle != SPXHANDLE_INVALID) + { + ::dialog_service_connector_handle_release(m_handle); + SPX_DBG_TRACE_VERBOSE("%s: m_handle=0x%8p", __FUNCTION__, (void*)m_handle); + m_handle = SPXHANDLE_INVALID; + } + } + + /// + /// Creates a dialog service connector from a and an . + /// Users should use this function to create a dialog service connector. + /// + /// Dialog service config. + /// Audio config. + /// The shared smart pointer of the created dialog service connector. + /// + /// + /// auto audioConfig = Audio::AudioConfig::FromDefaultMicrophoneInput(); + /// auto config = CustomCommandsConfig::FromAuthorizationToken("my_app_id","my_auth_token", "my_region"); + /// auto connector = DialogServiceConnector::FromConfig(config, audioConfig); + /// + /// + /// + /// When speaking of we are referring to one of the classes that inherit from it. + /// The specific class to be used depends on the dialog backend being used: + ///
    + ///
  • for DirectLineSpeech
  • + ///
  • for CustomCommands
  • + ///
+ ///
+ static std::shared_ptr FromConfig(std::shared_ptr connectorConfig, std::shared_ptr audioConfig = nullptr) + { + SPXRECOHANDLE h_connector; + SPX_THROW_ON_FAIL(::dialog_service_connector_create_dialog_service_connector_from_config( + &h_connector, + Utils::HandleOrInvalid(connectorConfig), + Utils::HandleOrInvalid(audioConfig) + )); + return std::shared_ptr { new DialogServiceConnector(h_connector) }; + } + + /// + /// Connects with the back end. + /// + /// An asynchronous operation that starts the connection. + std::future ConnectAsync() + { + auto keep_alive = this->shared_from_this(); + return std::async(std::launch::async, [keep_alive, this]() + { + SPX_THROW_ON_FAIL(::dialog_service_connector_connect(m_handle)); + }); + } + + /// + /// Disconnects from the back end. + /// + /// An asynchronous operation that starts the disconnection. + std::future DisconnectAsync() + { + auto keep_alive = this->shared_from_this(); + return std::async(std::launch::async, [keep_alive, this]() + { + SPX_THROW_ON_FAIL(::dialog_service_connector_disconnect(m_handle)); + }); + } + + /// + /// Sends an activity to the backing dialog. + /// + /// Activity to send + /// An asynchronous operation that starts the operation. + std::future SendActivityAsync(const std::string& activity) + { + auto keep_alive = this->shared_from_this(); + return std::async(std::launch::async, [keep_alive, activity, this]() + { + std::array buffer; + SPX_THROW_ON_FAIL(::dialog_service_connector_send_activity(m_handle, activity.c_str(), buffer.data())); + return std::string{ buffer.data() }; + }); + } + + /// + /// Initiates keyword recognition. + /// + /// Specifies the keyword model to be used. + /// An asynchronous operation that starts the operation. + std::future StartKeywordRecognitionAsync(std::shared_ptr model) + { + auto keep_alive = this->shared_from_this(); + auto h_model = Utils::HandleOrInvalid(model); + return std::async(std::launch::async, [keep_alive, h_model, this]() + { + SPX_THROW_ON_FAIL(dialog_service_connector_start_keyword_recognition(m_handle, h_model)); + }); + } + + /// + /// Stop keyword recognition. + /// + /// An asynchronous operation that starts the operation. + std::future StopKeywordRecognitionAsync() + { + auto keep_alive = this->shared_from_this(); + return std::async(std::launch::async, [keep_alive, this]() + { + SPX_THROW_ON_FAIL(dialog_service_connector_stop_keyword_recognition(m_handle)); + }); + } + + /// + /// Starts a listening session that will terminate after the first utterance. + /// + /// An asynchronous operation that starts the operation. + std::future> ListenOnceAsync() + { + auto keep_alive = this->shared_from_this(); + return std::async(std::launch::async, [keep_alive, this]() + { + SPX_INIT_HR(hr); + + SPXRECOHANDLE h_result = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(dialog_service_connector_listen_once(m_handle, &h_result)); + + return std::make_shared(h_result); + }); + } + + /// + /// Requests that an active listening operation immediately finish, interrupting any ongoing + /// speaking, and provide a result reflecting whatever audio data has been captured so far. + /// + /// A task representing the asynchronous operation that stops an active listening session. + std::future StopListeningAsync() + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this]() -> void { + SPX_INIT_HR(hr); + // close any unfinished previous attempt + SPX_THROW_ON_FAIL(hr = speechapi_async_handle_release(m_hasyncStopContinuous)); + SPX_EXITFN_ON_FAIL(hr = dialog_service_connector_stop_listening_async(m_handle, &m_hasyncStopContinuous)); + SPX_EXITFN_ON_FAIL(hr = speechapi_async_wait_for(m_hasyncStopContinuous, UINT32_MAX)); + + SPX_EXITFN_CLEANUP: + auto releaseHr = speechapi_async_handle_release(m_hasyncStopContinuous); + SPX_REPORT_ON_FAIL(releaseHr); + m_hasyncStopContinuous = SPXHANDLE_INVALID; + + SPX_THROW_ON_FAIL(hr); + }); + + return future; + } + + /// + /// Sets the authorization token that will be used for connecting to the service. + /// Note: The caller needs to ensure that the authorization token is valid. Before the authorization token + /// expires, the caller needs to refresh it by calling this setter with a new valid token. + /// Otherwise, the connector will encounter errors during its operation. + /// + /// The authorization token. + void SetAuthorizationToken(const SPXSTRING& token) + { + Properties.SetProperty(PropertyId::SpeechServiceAuthorization_Token, token); + } + + /// + /// Gets the authorization token. + /// + /// Authorization token + SPXSTRING GetAuthorizationToken() + { + return Properties.GetProperty(PropertyId::SpeechServiceAuthorization_Token, SPXSTRING()); + } + + /// + /// Sets a JSON template that will be provided to the speech service for the next conversation. The service will + /// attempt to merge this template into all activities sent to the dialog backend, whether originated by the + /// client with SendActivityAsync or generated by the service, as is the case with speech-to-text results. + /// + /// + /// The activity payload, as a JSON string, to be merged into all applicable activity messages. + /// + void SetSpeechActivityTemplate(const SPXSTRING& activityTemplate) + { + Properties.SetProperty(PropertyId::Conversation_Speech_Activity_Template, activityTemplate); + } + + /// + /// Gets the JSON template that will be provided to the speech service for the next conversation. The service will + /// attempt to merge this template into all activities sent to the dialog backend, whether originated by the + /// client with SendActivityAsync or generated by the service, as is the case with speech-to-text results. + /// + /// The JSON activity template currently set that will be used on subsequent requests. + SPXSTRING GetSpeechActivityTemplate() + { + return Properties.GetProperty(PropertyId::Conversation_Speech_Activity_Template, SPXSTRING()); + } + + /// + /// Signal for events containing speech recognition results. + /// + EventSignal Recognized; + + /// + /// Signal for events containing intermediate recognition results. + /// + EventSignal Recognizing; + + /// + /// Signals that indicates the start of a listening session. + /// + EventSignal SessionStarted; + + /// + /// Signal that indicates the end of a listening session. + /// + EventSignal SessionStopped; + + /// + /// Signal that indicates the first detection of speech data in the current phrase. + /// + EventSignal SpeechStartDetected; + + /// + /// Signal that indicates the detected end of the current phrase's speech data. + /// + EventSignal SpeechEndDetected; + + /// + /// Signal for events relating to the cancellation of an interaction. The event indicates if the reason is a direct cancellation or an error. + /// + EventSignal Canceled; + + /// + /// Signals that an activity was received from the backend + /// + EventSignal ActivityReceived; + + /// + /// Signals that a turn status update was received from the backend + /// + EventSignal TurnStatusReceived; + +private: + /*! \cond PROTECTED */ + template + std::function&)> Callback(F f) + { + return [=, this](const EventSignal& evt) + { + (this->*f)(evt); + }; + } + + static void FireEvent_Recognized(SPXRECOHANDLE, SPXEVENTHANDLE h_event, void* pv_context) + { + auto keep_alive = static_cast(pv_context)->shared_from_this(); + SpeechRecognitionEventArgs event{ h_event }; + keep_alive->Recognized.Signal(event); + /* Not releasing the handle as SpeechRecognitionEventArgs manages it */ + } + + static void FireEvent_Recognizing(SPXRECOHANDLE, SPXEVENTHANDLE h_event, void* pv_context) + { + auto keep_alive = static_cast(pv_context)->shared_from_this(); + SpeechRecognitionEventArgs event{ h_event }; + keep_alive->Recognizing.Signal(event); + /* Not releasing the handle as SpeechRecognitionEventArgs manages it */ + } + + void RecognizerEventConnectionChanged(const EventSignal& reco_event) + { + if (m_handle != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_handle=0x%8p", __FUNCTION__, (void*)m_handle); + SPX_DBG_TRACE_VERBOSE_IF(!::dialog_service_connector_handle_is_valid(m_handle), "%s: m_handle is INVALID!!!", __FUNCTION__); + + if (&reco_event == &Recognizing) + { + ::dialog_service_connector_recognizing_set_callback(m_handle, Recognizing.IsConnected() ? DialogServiceConnector::FireEvent_Recognizing : nullptr, this); + } + else if (&reco_event == &Recognized) + { + ::dialog_service_connector_recognized_set_callback(m_handle, Recognized.IsConnected() ? DialogServiceConnector::FireEvent_Recognized : nullptr, this); + } + } + } + + static void FireEvent_SessionStarted(SPXRECOHANDLE, SPXEVENTHANDLE h_event, void* pv_context) + { + auto keep_alive = static_cast(pv_context)->shared_from_this(); + SessionEventArgs event{ h_event }; + keep_alive->SessionStarted.Signal(event); + + SPX_DBG_ASSERT(::recognizer_event_handle_is_valid(h_event)); + /* Releasing the event handle as SessionEventArgs doesn't keep the handle */ + ::recognizer_event_handle_release(h_event); + } + + static void FireEvent_SessionStopped(SPXRECOHANDLE, SPXEVENTHANDLE h_event, void* pv_context) + { + auto keep_alive = static_cast(pv_context)->shared_from_this(); + SessionEventArgs event{ h_event }; + keep_alive->SessionStopped.Signal(event); + + SPX_DBG_ASSERT(::recognizer_event_handle_is_valid(h_event)); + /* Releasing the event handle as SessionEventArgs doesn't keep the handle */ + ::recognizer_event_handle_release(h_event); + } + + void SessionEventConnectionChanged(const EventSignal& session_event) + { + if (m_handle != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_handle=0x%8p", __FUNCTION__, (void*)m_handle); + SPX_DBG_TRACE_VERBOSE_IF(!::dialog_service_connector_handle_is_valid(m_handle), "%s: m_handle is INVALID!!!", __FUNCTION__); + + if (&session_event == &SessionStarted) + { + ::dialog_service_connector_session_started_set_callback(m_handle, SessionStarted.IsConnected() ? DialogServiceConnector::FireEvent_SessionStarted : nullptr, this); + } + else if (&session_event == &SessionStopped) + { + ::dialog_service_connector_session_stopped_set_callback(m_handle, SessionStopped.IsConnected() ? DialogServiceConnector::FireEvent_SessionStopped : nullptr, this); + } + } + } + + static void FireEvent_SpeechStartDetected(SPXRECOHANDLE, SPXEVENTHANDLE h_event, void* pv_context) + { + auto keep_alive = static_cast(pv_context)->shared_from_this(); + RecognitionEventArgs event{ h_event }; + keep_alive->SpeechStartDetected.Signal(event); + + SPX_DBG_ASSERT(::recognizer_event_handle_is_valid(h_event)); + /* Releasing the event handle as RecognitionEventArgs doesn't manage handle lifetime */ + ::recognizer_event_handle_release(h_event); + } + + static void FireEvent_SpeechEndDetected(SPXRECOHANDLE, SPXEVENTHANDLE h_event, void* pv_context) + { + auto keep_alive = static_cast(pv_context)->shared_from_this(); + RecognitionEventArgs event{ h_event }; + keep_alive->SpeechEndDetected.Signal(event); + + SPX_DBG_ASSERT(::recognizer_event_handle_is_valid(h_event)); + /* Releasing the event handle as RecognitionEventArgs doesn't manage handle lifetime */ + ::recognizer_event_handle_release(h_event); + } + + void SpeechDetectionEventConnectionChanged(const EventSignal& speech_detection_event) + { + if (m_handle != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_handle=0x%8p", __FUNCTION__, (void*)m_handle); + SPX_DBG_TRACE_VERBOSE_IF(!::dialog_service_connector_handle_is_valid(m_handle), "%s: m_handle is INVALID!!!", __FUNCTION__); + + if (&speech_detection_event == &SpeechStartDetected) + { + ::dialog_service_connector_speech_start_detected_set_callback(m_handle, SpeechStartDetected.IsConnected() ? DialogServiceConnector::FireEvent_SpeechStartDetected : nullptr, this); + } + else if (&speech_detection_event == &SpeechEndDetected) + { + ::dialog_service_connector_speech_end_detected_set_callback(m_handle, SpeechEndDetected.IsConnected() ? DialogServiceConnector::FireEvent_SpeechEndDetected : nullptr, this); + } + } + } + + static void FireEvent_Canceled(SPXRECOHANDLE, SPXEVENTHANDLE h_event, void* pv_context) + { + auto keep_alive = static_cast(pv_context)->shared_from_this(); + SpeechRecognitionCanceledEventArgs event{ h_event }; + keep_alive->Canceled.Signal(event); + /* Not releasing the handle as SpeechRecognitionCanceledEventArgs manages it */ + } + + void CanceledEventConnectionChanged(const EventSignal& canceled_event) + { + if (m_handle != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_handle=0x%8p", __FUNCTION__, (void*)m_handle); + SPX_DBG_TRACE_VERBOSE_IF(!::dialog_service_connector_handle_is_valid(m_handle), "%s: m_handle is INVALID!!!", __FUNCTION__); + + if (&canceled_event == &Canceled) + { + ::dialog_service_connector_canceled_set_callback(m_handle, Canceled.IsConnected() ? DialogServiceConnector::FireEvent_Canceled : nullptr, this); + } + } + } + + static void FireEvent_ActivityReceived(SPXRECOHANDLE, SPXEVENTHANDLE h_event, void* pv_context) + { + auto keep_alive = static_cast(pv_context)->shared_from_this(); + ActivityReceivedEventArgs event{ h_event }; + keep_alive->ActivityReceived.Signal(event); + /* Not releasing the handle as ActivityReceivedEventArgs manages it */ + } + + void ActivityReceivedConnectionChanged(const EventSignal& activity_event) + { + if (m_handle != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_hreco=0x%8p", __FUNCTION__, (void*)m_handle); + SPX_DBG_TRACE_VERBOSE_IF(!::dialog_service_connector_handle_is_valid(m_handle), "%s: m_handle is INVALID!!!", __FUNCTION__); + + if (&activity_event == &ActivityReceived) + { + ::dialog_service_connector_activity_received_set_callback(m_handle, ActivityReceived.IsConnected() ? DialogServiceConnector::FireEvent_ActivityReceived : nullptr, this); + } + } + } + + static void FireEvent_TurnStatusReceived(SPXRECOHANDLE, SPXEVENTHANDLE h_event, void* pv_context) + { + auto keep_alive = static_cast(pv_context)->shared_from_this(); + TurnStatusReceivedEventArgs event{ h_event }; + keep_alive->TurnStatusReceived.Signal(event); + /* Not releasing the handle as TurnStatusReceivedEventArgs manages it */ + } + + void TurnStatusReceivedConnectionChanged(const EventSignal& turn_status_event) + { + if (m_handle != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_hreco=0x%8p", __FUNCTION__, (void*)m_handle); + SPX_DBG_TRACE_VERBOSE_IF(!::dialog_service_connector_handle_is_valid(m_handle), "%s: m_handle is INVALID!!!", __FUNCTION__); + + if (&turn_status_event == &TurnStatusReceived) + { + ::dialog_service_connector_turn_status_received_set_callback(m_handle, TurnStatusReceived.IsConnected() ? DialogServiceConnector::FireEvent_TurnStatusReceived : nullptr, this); + } + } + } + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXRECOHANDLE h_connector) : + PropertyCollection( + [=](){ + SPXPROPERTYBAGHANDLE h_prop_bag = SPXHANDLE_INVALID; + dialog_service_connector_get_property_bag(h_connector, &h_prop_bag); + return h_prop_bag; + }()) + { + } + }; + + inline explicit DialogServiceConnector(SPXRECOHANDLE handle) : + Recognized{ Callback(&DialogServiceConnector::RecognizerEventConnectionChanged) }, + Recognizing{ Callback(&DialogServiceConnector::RecognizerEventConnectionChanged) }, + SessionStarted{ Callback(&DialogServiceConnector::SessionEventConnectionChanged) }, + SessionStopped{ Callback(&DialogServiceConnector::SessionEventConnectionChanged) }, + SpeechStartDetected{ Callback(&DialogServiceConnector::SpeechDetectionEventConnectionChanged) }, + SpeechEndDetected{ Callback(&DialogServiceConnector::SpeechDetectionEventConnectionChanged) }, + Canceled{ Callback(&DialogServiceConnector::CanceledEventConnectionChanged) }, + ActivityReceived{ Callback(&DialogServiceConnector::ActivityReceivedConnectionChanged) }, + TurnStatusReceived{ Callback(&DialogServiceConnector::TurnStatusReceivedConnectionChanged) }, + m_handle{ handle }, + m_properties{ handle }, + Properties{ m_properties } + { + } + +private: + friend class Microsoft::CognitiveServices::Speech::Connection; + SPXRECOHANDLE m_handle; + SPXASYNCHANDLE m_hasyncStopContinuous; + + PrivatePropertyCollection m_properties; + /*! \endcond */ +public: + /// + /// A collection of properties and their values defined for this . + /// + PropertyCollection& Properties; +}; + +} } } } diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_dialog_service_connector_eventargs.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_dialog_service_connector_eventargs.h new file mode 100644 index 0000000..f55f611 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_dialog_service_connector_eventargs.h @@ -0,0 +1,148 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once + +#include +#include + +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Dialog { + +// Forward declarations +class DialogServiceConnector; + +/// +/// Class for activity received event arguments. +/// +class ActivityReceivedEventArgs: public std::enable_shared_from_this +{ +public: + friend DialogServiceConnector; + /// + /// Releases the event. + /// + inline ~ActivityReceivedEventArgs() + { + SPX_THROW_ON_FAIL(::dialog_service_connector_activity_received_event_release(m_handle)); + } + + /// + /// Gets the activity associated with the event. + /// + /// The serialized activity activity. + inline std::string GetActivity() const + { + size_t size; + SPX_THROW_ON_FAIL(::dialog_service_connector_activity_received_event_get_activity_size(m_handle, &size)); + auto ptr = std::make_unique(size + 1); + SPX_THROW_ON_FAIL(::dialog_service_connector_activity_received_event_get_activity(m_handle, ptr.get(), size + 1)); + return std::string{ ptr.get() }; + } + + /// + /// Gets the audio associated with the event. + /// + /// The audio. + inline std::shared_ptr GetAudio() const + { + SPXAUDIOSTREAMHANDLE h_audio{ SPXHANDLE_INVALID }; + SPX_THROW_ON_FAIL(::dialog_service_connector_activity_received_event_get_audio(m_handle, &h_audio)); + if (h_audio == SPXHANDLE_INVALID) + { + return nullptr; + } + return std::shared_ptr(new Audio::PullAudioOutputStream(h_audio) ); + } + + /// + /// Checks if the event contains audio. + /// + /// True if the event contains audio, false otherwise. + inline bool HasAudio() const + { + return ::dialog_service_connector_activity_received_event_has_audio(m_handle); + } +private: + /*! \cond PROTECTED */ + inline ActivityReceivedEventArgs(SPXEVENTHANDLE h_event) : m_handle{ h_event } + { + } + + SPXEVENTHANDLE m_handle; + /*! \endcond */ +}; + +/// +/// Class for turn status event arguments. +/// +class TurnStatusReceivedEventArgs : public std::enable_shared_from_this +{ +public: + friend DialogServiceConnector; + /// + /// Releases the event. + /// + inline ~TurnStatusReceivedEventArgs() + { + SPX_THROW_ON_FAIL(::dialog_service_connector_turn_status_received_release(m_handle)); + } + + /// + /// Retrieves the interaction ID associated with this turn status event. Interaction generally correspond + /// to a single input signal (e.g. voice utterance) or data/activity transaction and will correlate to + /// 'replyToId' fields in Bot Framework activities. + /// + /// The interaction ID associated with the turn status. + inline std::string GetInteractionId() const + { + size_t size = 0; + SPX_THROW_ON_FAIL(::dialog_service_connector_turn_status_received_get_interaction_id_size(m_handle, &size)); + auto ptr = std::make_unique(size + 1); + SPX_THROW_ON_FAIL(::dialog_service_connector_turn_status_received_get_interaction_id(m_handle, ptr.get(), size + 1)); + return std::string{ ptr.get() }; + } + + /// + /// Retrieves the conversation ID associated with this turn status event. Conversations may span multiple + /// interactions and are the unit which a client may request resume/retry upon. + /// + /// The conversation ID associated with the turn status. + inline std::string GetConversationId() const + { + size_t size = 0; + SPX_THROW_ON_FAIL(::dialog_service_connector_turn_status_received_get_conversation_id_size(m_handle, &size)); + auto ptr = std::make_unique(size + 1); + SPX_THROW_ON_FAIL(::dialog_service_connector_turn_status_received_get_conversation_id(m_handle, ptr.get(), size + 1)); + return std::string{ ptr.get() }; + } + + /// + /// Retrieves the numeric status code associated with this turn status event. These generally correspond to + /// standard HTTP status codes such as 200 (OK), 400 (Failure/Bad Request), and 429 (Timeout/Throttled). + /// + /// The status code associated with this event, analolgous to standard HTTP codes. + inline int GetStatusCode() const + { + int cApiStatus = 404; + SPX_THROW_ON_FAIL(::dialog_service_connector_turn_status_received_get_status(m_handle, &cApiStatus)); + return cApiStatus; + } + +private: + /*! \cond PROTECTED */ + inline TurnStatusReceivedEventArgs(SPXEVENTHANDLE h_event) : m_handle{ h_event } + { + } + + SPXEVENTHANDLE m_handle; + /*! \endcond */ +}; + +} } } } diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_embedded_speech_config.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_embedded_speech_config.h new file mode 100644 index 0000000..61454a2 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_embedded_speech_config.h @@ -0,0 +1,324 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_embedded_speech_config.h: Public API declarations for EmbeddedSpeechConfig C++ class +// + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Class that defines embedded (offline) speech configuration. +/// +class EmbeddedSpeechConfig +{ +protected: + /*! \cond PROTECTED */ + + SpeechConfig m_config; + + /*! \endcond */ + +public: + /// + /// Internal operator used to get the underlying handle value. + /// + /// A handle. + explicit operator SPXSPEECHCONFIGHANDLE() const + { + return static_cast(m_config); + } + + /// + /// Creates an instance of the embedded speech config with a specified offline model path. + /// + /// The folder path to search for offline models. + /// This can be a root path under which several models are located in subfolders, + /// or a direct path to a specific model folder. + /// + /// A shared pointer to the new embedded speech config instance. + static std::shared_ptr FromPath(const SPXSTRING& path) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, path.empty()); + SPXSPEECHCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + + SPX_THROW_ON_FAIL(embedded_speech_config_create(&hconfig)); + SPX_THROW_ON_FAIL(embedded_speech_config_add_path(hconfig, Utils::ToUTF8(path).c_str())); + + auto ptr = new EmbeddedSpeechConfig(hconfig); + return std::shared_ptr(ptr); + } + + /// + /// Creates an instance of the embedded speech config with specified offline model paths. + /// + /// The folder paths to search for offline models. + /// These can be root paths under which several models are located in subfolders, + /// or direct paths to specific model folders. + /// + /// A shared pointer to the new embedded speech config instance. + static std::shared_ptr FromPaths(const std::vector& paths) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, paths.empty()); + SPXSPEECHCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + + SPX_THROW_ON_FAIL(embedded_speech_config_create(&hconfig)); + for (const SPXSTRING& path : paths) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, path.empty()); + SPX_THROW_ON_FAIL(embedded_speech_config_add_path(hconfig, Utils::ToUTF8(path).c_str())); + } + + auto ptr = new EmbeddedSpeechConfig(hconfig); + return std::shared_ptr(ptr); + } + + /// + /// Gets a list of available speech recognition models. + /// + /// Speech recognition model info. + std::vector> GetSpeechRecognitionModels() + { + std::vector> models; + + uint32_t numModels = 0; + SPX_THROW_ON_FAIL(embedded_speech_config_get_num_speech_reco_models(static_cast(m_config), &numModels)); + + for (uint32_t i = 0; i < numModels; i++) + { + SPXSPEECHRECOMODELHANDLE hmodel = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(embedded_speech_config_get_speech_reco_model(static_cast(m_config), i, &hmodel)); + + auto model = std::make_shared(hmodel); + models.push_back(model); + } + + return models; + } + + /// + /// Sets the model for speech recognition. + /// + /// The model name. + /// The license text. + void SetSpeechRecognitionModel(const SPXSTRING& name, const SPXSTRING& license) + { + SPX_THROW_ON_FAIL(embedded_speech_config_set_speech_recognition_model( + static_cast(m_config), Utils::ToUTF8(name).c_str(), Utils::ToUTF8(license).c_str())); + } + + /// + /// Gets the model name for speech recognition. + /// + /// The speech recognition model name. + SPXSTRING GetSpeechRecognitionModelName() const + { + return GetProperty(PropertyId::SpeechServiceConnection_RecoModelName); + } + + /// + /// Sets the speech recognition output format. + /// + /// Speech recognition output format (simple or detailed). + void SetSpeechRecognitionOutputFormat(OutputFormat format) + { + m_config.SetOutputFormat(format); + } + + /// + /// Gets the speech recognition output format. + /// + /// Speech recognition output format (simple or detailed). + OutputFormat GetSpeechRecognitionOutputFormat() const + { + return m_config.GetOutputFormat(); + } + + /// + /// Sets the profanity option. This can be used to remove profane words or mask them. + /// + /// Profanity option value. + void SetProfanity(ProfanityOption profanity) + { + m_config.SetProfanity(profanity); + } + + /// + /// Sets the voice for embedded speech synthesis. + /// + /// The voice name of the embedded speech synthesis. + /// The license text. + void SetSpeechSynthesisVoice(const SPXSTRING& name, const SPXSTRING& license) + { + SPX_THROW_ON_FAIL(embedded_speech_config_set_speech_synthesis_voice( + static_cast(m_config), Utils::ToUTF8(name).c_str(), Utils::ToUTF8(license).c_str())); + } + + /// + /// Gets the voice name for embedded speech synthesis. + /// + /// The speech synthesis model name, i.e. the voice name. + SPXSTRING GetSpeechSynthesisVoiceName() const + { + return GetProperty(PropertyId::SpeechServiceConnection_SynthOfflineVoice); + } + + /// + /// Sets the speech synthesis output format (e.g. Riff16Khz16BitMonoPcm). + /// + /// Specifies the output format ID + void SetSpeechSynthesisOutputFormat(SpeechSynthesisOutputFormat formatId) + { + m_config.SetSpeechSynthesisOutputFormat(formatId); + } + + /// + /// Gets the speech synthesis output format. + /// + /// The speech synthesis output format. + SPXSTRING GetSpeechSynthesisOutputFormat() const + { + return m_config.GetSpeechSynthesisOutputFormat(); + } + + /// + /// Gets a list of available speech translation models. + /// + /// Speech translation model info. + std::vector> GetSpeechTranslationModels() + { + std::vector> models; + + uint32_t numModels = 0; + SPX_THROW_ON_FAIL(embedded_speech_config_get_num_speech_translation_models(static_cast(m_config), &numModels)); + + for (uint32_t i = 0; i < numModels; i++) + { + SPXSPEECHRECOMODELHANDLE hmodel = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(embedded_speech_config_get_speech_translation_model(static_cast(m_config), i, &hmodel)); + + auto model = std::make_shared(hmodel); + models.push_back(model); + } + + return models; + } + + /// + /// Sets the model for speech translation. + /// + /// Model name. + /// License text. + void SetSpeechTranslationModel(const SPXSTRING& name, const SPXSTRING& license) + { + SPX_THROW_ON_FAIL(embedded_speech_config_set_speech_translation_model( + static_cast(m_config), Utils::ToUTF8(name).c_str(), Utils::ToUTF8(license).c_str())); + } + + /// + /// Gets the model name for speech translation. + /// + /// The speech translation model name. + SPXSTRING GetSpeechTranslationModelName() const + { + return GetProperty(PropertyId::SpeechTranslation_ModelName); + } + + /// + /// Sets the model for keyword recognition. + /// This is for customer specific models that are tailored for detecting + /// wake words and direct commands. + /// + /// Model name. + /// License text. + void SetKeywordRecognitionModel(const SPXSTRING& name, const SPXSTRING& license) + { + SPX_THROW_ON_FAIL(embedded_speech_config_set_keyword_recognition_model( + static_cast(m_config), Utils::ToUTF8(name).c_str(), Utils::ToUTF8(license).c_str())); + } + + /// + /// Gets the model name for keyword recognition. + /// + /// The keyword recognition model name. + SPXSTRING GetKeywordRecognitionModelName() const + { + return GetProperty(PropertyId::KeywordRecognition_ModelName); + } + + /// + /// Sets a property value by name. + /// + /// The property name. + /// The property value. + void SetProperty(const SPXSTRING& name, const SPXSTRING& value) + { + m_config.SetProperty(name, value); + } + + /// + /// Sets a property value by ID. + /// + /// The property id. + /// The property value. + void SetProperty(PropertyId id, const SPXSTRING& value) + { + m_config.SetProperty(id, value); + } + + /// + /// Gets a property value by name. + /// + /// The parameter name. + /// The property value. + SPXSTRING GetProperty(const SPXSTRING& name) const + { + return m_config.GetProperty(name); + } + + /// + /// Gets a property value by ID. + /// + /// The parameter id. + /// The property value. + SPXSTRING GetProperty(PropertyId id) const + { + return m_config.GetProperty(id); + } + + /// + /// Destructs the object. + /// + virtual ~EmbeddedSpeechConfig() = default; + +protected: + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + inline explicit EmbeddedSpeechConfig(SPXSPEECHCONFIGHANDLE hconfig) : m_config(hconfig) + { + } + + /*! \endcond */ + +private: + DISABLE_COPY_AND_MOVE(EmbeddedSpeechConfig); + + }; + +}}} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_enums.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_enums.h new file mode 100644 index 0000000..2f54177 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_enums.h @@ -0,0 +1,1685 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_enums.h: Public API declarations for C++ enumerations +// + +#pragma once + +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +constexpr const char* TrueString = "true"; +constexpr const char* FalseString = "false"; +constexpr const char CommaDelim = ','; + +/// +/// Defines speech property ids. +/// Changed in version 1.4.0. +/// +enum class PropertyId +{ + /// + /// The Cognitive Services Speech Service subscription key. If you are using an intent recognizer, you need + /// to specify the LUIS endpoint key for your particular LUIS app. Under normal circumstances, you shouldn't + /// have to use this property directly. + /// Instead, use . + /// + SpeechServiceConnection_Key = 1000, + + /// + /// The Cognitive Services Speech Service endpoint (url). Under normal circumstances, you shouldn't + /// have to use this property directly. + /// Instead, use . + /// NOTE: This endpoint is not the same as the endpoint used to obtain an access token. + /// + SpeechServiceConnection_Endpoint = 1001, + + /// + /// The Cognitive Services Speech Service region. Under normal circumstances, you shouldn't have to + /// use this property directly. + /// Instead, use , , + /// , . + /// + SpeechServiceConnection_Region = 1002, + + /// + /// The Cognitive Services Speech Service authorization token (aka access token). Under normal circumstances, + /// you shouldn't have to use this property directly. + /// Instead, use , + /// , , + /// . + /// + SpeechServiceAuthorization_Token = 1003, + + /// + /// The Cognitive Services Speech Service authorization type. Currently unused. + /// + SpeechServiceAuthorization_Type = 1004, + + /// + /// The Cognitive Services Custom Speech or Custom Voice Service endpoint id. Under normal circumstances, you shouldn't + /// have to use this property directly. + /// Instead use . + /// NOTE: The endpoint id is available in the Custom Speech Portal, listed under Endpoint Details. + /// + SpeechServiceConnection_EndpointId = 1005, + + /// + /// The Cognitive Services Speech Service host (url). Under normal circumstances, you shouldn't + /// have to use this property directly. + /// Instead, use . + /// + SpeechServiceConnection_Host = 1006, + + /// + /// The host name of the proxy server used to connect to the Cognitive Services Speech Service. Under normal circumstances, + /// you shouldn't have to use this property directly. + /// Instead, use . + /// NOTE: This property id was added in version 1.1.0. + /// + SpeechServiceConnection_ProxyHostName = 1100, + + /// + /// The port of the proxy server used to connect to the Cognitive Services Speech Service. Under normal circumstances, + /// you shouldn't have to use this property directly. + /// Instead, use . + /// NOTE: This property id was added in version 1.1.0. + /// + SpeechServiceConnection_ProxyPort = 1101, + + /// + /// The user name of the proxy server used to connect to the Cognitive Services Speech Service. Under normal circumstances, + /// you shouldn't have to use this property directly. + /// Instead, use . + /// NOTE: This property id was added in version 1.1.0. + /// + SpeechServiceConnection_ProxyUserName = 1102, + + /// + /// The password of the proxy server used to connect to the Cognitive Services Speech Service. Under normal circumstances, + /// you shouldn't have to use this property directly. + /// Instead, use . + /// NOTE: This property id was added in version 1.1.0. + /// + SpeechServiceConnection_ProxyPassword = 1103, + + /// + /// The URL string built from speech configuration. + /// This property is intended to be read-only. The SDK is using it internally. + /// NOTE: Added in version 1.5.0. + /// + SpeechServiceConnection_Url = 1104, + + /// + /// Specifies the list of hosts for which proxies should not be used. This setting overrides all other configurations. + /// Hostnames are separated by commas and are matched in a case-insensitive manner. Wildcards are not supported. + /// + SpeechServiceConnection_ProxyHostBypass = 1105, + + /// + /// The list of comma separated languages used as target translation languages. Under normal circumstances, + /// you shouldn't have to use this property directly. Instead use + /// and . + /// + SpeechServiceConnection_TranslationToLanguages = 2000, + + /// + /// The name of the Cognitive Service Text to Speech Service voice. Under normal circumstances, you shouldn't have to use this + /// property directly. Instead use . + /// NOTE: Valid voice names can be found here. + /// + SpeechServiceConnection_TranslationVoice = 2001, + + /// + /// Translation features. For internal use. + /// + SpeechServiceConnection_TranslationFeatures = 2002, + + /// + /// The Language Understanding Service region. Under normal circumstances, you shouldn't have to use this property directly. + /// Instead use . + /// + SpeechServiceConnection_IntentRegion = 2003, + + /// + /// The Cognitive Services Speech Service recognition mode. Can be "INTERACTIVE", "CONVERSATION", "DICTATION". + /// This property is intended to be read-only. The SDK is using it internally. + /// + SpeechServiceConnection_RecoMode = 3000, + + /// + /// The spoken language to be recognized (in BCP-47 format). Under normal circumstances, you shouldn't have to use this property + /// directly. + /// Instead, use . + /// + SpeechServiceConnection_RecoLanguage = 3001, + + /// + /// The session id. This id is a universally unique identifier (aka UUID) representing a specific binding of an audio input stream + /// and the underlying speech recognition instance to which it is bound. Under normal circumstances, you shouldn't have to use this + /// property directly. + /// Instead use . + /// + Speech_SessionId = 3002, + + /// + /// The query parameters provided by users. They will be passed to service as URL query parameters. + /// Added in version 1.5.0 + /// + SpeechServiceConnection_UserDefinedQueryParameters = 3003, + + /// + /// The string to specify the backend to be used for speech recognition; + /// allowed options are online and offline. + /// Under normal circumstances, you shouldn't use this property directly. + /// Currently the offline option is only valid when EmbeddedSpeechConfig is used. + /// Added in version 1.19.0 + /// + SpeechServiceConnection_RecoBackend = 3004, + + /// + /// The name of the model to be used for speech recognition. + /// Under normal circumstances, you shouldn't use this property directly. + /// Currently this is only valid when EmbeddedSpeechConfig is used. + /// Added in version 1.19.0 + /// + SpeechServiceConnection_RecoModelName = 3005, + + /// + /// This property is deprecated. + /// + SpeechServiceConnection_RecoModelKey = 3006, + + /// + /// The path to the ini file of the model to be used for speech recognition. + /// Under normal circumstances, you shouldn't use this property directly. + /// Currently this is only valid when EmbeddedSpeechConfig is used. + /// Added in version 1.19.0 + /// + SpeechServiceConnection_RecoModelIniFile = 3007, + + /// + /// The spoken language to be synthesized (e.g. en-US) + /// Added in version 1.4.0 + /// + SpeechServiceConnection_SynthLanguage = 3100, + + /// + /// The name of the TTS voice to be used for speech synthesis + /// Added in version 1.4.0 + /// + SpeechServiceConnection_SynthVoice = 3101, + + /// + /// The string to specify TTS output audio format + /// Added in version 1.4.0 + /// + SpeechServiceConnection_SynthOutputFormat = 3102, + + /// + /// Indicates if use compressed audio format for speech synthesis audio transmission. + /// This property only affects when SpeechServiceConnection_SynthOutputFormat is set to a pcm format. + /// If this property is not set and GStreamer is available, SDK will use compressed format for synthesized audio transmission, + /// and decode it. You can set this property to "false" to use raw pcm format for transmission on wire. + /// Added in version 1.16.0 + /// + SpeechServiceConnection_SynthEnableCompressedAudioTransmission = 3103, + + /// + /// The string to specify TTS backend; valid options are online and offline. + /// Under normal circumstances, you shouldn't have to use this property directly. + /// Instead, use or + /// to set the synthesis backend to offline. + /// Added in version 1.19.0 + /// + SpeechServiceConnection_SynthBackend = 3110, + + /// + /// The data file path(s) for offline synthesis engine; only valid when synthesis backend is offline. + /// Under normal circumstances, you shouldn't have to use this property directly. + /// Instead, use or . + /// Added in version 1.19.0 + /// + SpeechServiceConnection_SynthOfflineDataPath = 3112, + + /// + /// The name of the offline TTS voice to be used for speech synthesis + /// Under normal circumstances, you shouldn't use this property directly. + /// Instead, use and . + /// Added in version 1.19.0 + /// + SpeechServiceConnection_SynthOfflineVoice = 3113, + + /// + /// This property is deprecated. + /// + SpeechServiceConnection_SynthModelKey = 3114, + + /// + /// The Cognitive Services Speech Service voices list api endpoint (url). Under normal circumstances, + /// you don't need to specify this property, SDK will construct it based on the region/host/endpoint of . + /// Added in version 1.16.0 + /// + SpeechServiceConnection_VoicesListEndpoint = 3130, + + /// + /// The initial silence timeout value (in milliseconds) used by the service. + /// Added in version 1.5.0 + /// + SpeechServiceConnection_InitialSilenceTimeoutMs = 3200, + + /// + /// The end silence timeout value (in milliseconds) used by the service. + /// Added in version 1.5.0 + /// + SpeechServiceConnection_EndSilenceTimeoutMs = 3201, + + /// + /// A boolean value specifying whether audio logging is enabled in the service or not. + /// Audio and content logs are stored either in Microsoft-owned storage, or in your own storage account linked + /// to your Cognitive Services subscription (Bring Your Own Storage (BYOS) enabled Speech resource). + /// Added in version 1.5.0. + /// + SpeechServiceConnection_EnableAudioLogging = 3202, + + /// + /// The speech service connection language identifier mode. + /// Can be "AtStart" (the default), or "Continuous". See [Language + /// Identification](https://aka.ms/speech/lid?pivots=programming-language-cpp) document. + /// Added in 1.25.0 + /// + SpeechServiceConnection_LanguageIdMode = 3205, + + /// + /// The speech service connection translation categoryId. + /// + SpeechServiceConnection_TranslationCategoryId = 3206, + + /// + /// The auto detect source languages + /// Added in version 1.8.0 + /// + SpeechServiceConnection_AutoDetectSourceLanguages = 3300, + + /// + /// The auto detect source language result + /// Added in version 1.8.0 + /// + SpeechServiceConnection_AutoDetectSourceLanguageResult = 3301, + + /// + /// The requested Cognitive Services Speech Service response output format (simple or detailed). Under normal circumstances, you shouldn't have + /// to use this property directly. + /// Instead use . + /// + SpeechServiceResponse_RequestDetailedResultTrueFalse = 4000, + + /// + /// The requested Cognitive Services Speech Service response output profanity level. Currently unused. + /// + SpeechServiceResponse_RequestProfanityFilterTrueFalse = 4001, + + /// + /// The requested Cognitive Services Speech Service response output profanity setting. + /// Allowed values are "masked", "removed", and "raw". + /// Added in version 1.5.0. + /// + SpeechServiceResponse_ProfanityOption = 4002, + + /// + /// A string value specifying which post processing option should be used by service. + /// Allowed values are "TrueText". + /// Added in version 1.5.0 + /// + SpeechServiceResponse_PostProcessingOption = 4003, + + /// + /// A boolean value specifying whether to include word-level timestamps in the response result. + /// Added in version 1.5.0 + /// + SpeechServiceResponse_RequestWordLevelTimestamps = 4004, + + /// + /// The number of times a word has to be in partial results to be returned. + /// Added in version 1.5.0 + /// + SpeechServiceResponse_StablePartialResultThreshold = 4005, + + /// + /// A string value specifying the output format option in the response result. Internal use only. + /// Added in version 1.5.0. + /// + SpeechServiceResponse_OutputFormatOption = 4006, + + /// + /// A boolean value specifying whether to include SNR (signal to noise ratio) in the response result. + /// Added in version 1.18.0 + /// + SpeechServiceResponse_RequestSnr = 4007, + + /// + /// A boolean value to request for stabilizing translation partial results by omitting words in the end. + /// Added in version 1.5.0. + /// + SpeechServiceResponse_TranslationRequestStablePartialResult = 4100, + + /// + /// A boolean value specifying whether to request WordBoundary events. + /// Added in version 1.21.0. + /// + SpeechServiceResponse_RequestWordBoundary = 4200, + + /// + /// A boolean value specifying whether to request punctuation boundary in WordBoundary Events. Default is true. + /// Added in version 1.21.0. + /// + SpeechServiceResponse_RequestPunctuationBoundary = 4201, + + /// + /// A boolean value specifying whether to request sentence boundary in WordBoundary Events. Default is false. + /// Added in version 1.21.0. + /// + SpeechServiceResponse_RequestSentenceBoundary = 4202, + + /// + /// A boolean value specifying whether the SDK should synchronize synthesis metadata events, + /// (e.g. word boundary, viseme, etc.) to the audio playback. This only takes effect when the audio is played through the SDK. + /// Default is true. + /// If set to false, the SDK will fire the events as they come from the service, which may be out of sync with the audio playback. + /// Added in version 1.31.0. + /// + SpeechServiceResponse_SynthesisEventsSyncToAudio = 4210, + + /// + /// The Cognitive Services Speech Service response output (in JSON format). This property is available on recognition result objects only. + /// + SpeechServiceResponse_JsonResult = 5000, + + /// + /// The Cognitive Services Speech Service error details (in JSON format). Under normal circumstances, you shouldn't have to + /// use this property directly. + /// Instead, use . + /// + SpeechServiceResponse_JsonErrorDetails = 5001, + + /// + /// The recognition latency in milliseconds. Read-only, available on final speech/translation/intent results. + /// This measures the latency between when an audio input is received by the SDK, and the moment the final result is received from the service. + /// The SDK computes the time difference between the last audio fragment from the audio input that is contributing to the final result, and the time the final result is received from the speech service. + /// Added in version 1.3.0. + /// + SpeechServiceResponse_RecognitionLatencyMs = 5002, + + /// + /// The recognition backend. Read-only, available on speech recognition results. + /// This indicates whether cloud (online) or embedded (offline) recognition was used to produce the result. + /// + SpeechServiceResponse_RecognitionBackend = 5003, + + /// + /// The speech synthesis first byte latency in milliseconds. Read-only, available on final speech synthesis results. + /// This measures the latency between when the synthesis is started to be processed, and the moment the first byte audio is available. + /// Added in version 1.17.0. + /// + SpeechServiceResponse_SynthesisFirstByteLatencyMs = 5010, + + /// + /// The speech synthesis all bytes latency in milliseconds. Read-only, available on final speech synthesis results. + /// This measures the latency between when the synthesis is started to be processed, and the moment the whole audio is synthesized. + /// Added in version 1.17.0. + /// + SpeechServiceResponse_SynthesisFinishLatencyMs = 5011, + + /// + /// The underrun time for speech synthesis in milliseconds. Read-only, available on results in SynthesisCompleted events. + /// This measures the total underrun time from is filled to synthesis completed. + /// Added in version 1.17.0. + /// + SpeechServiceResponse_SynthesisUnderrunTimeMs = 5012, + + /// + /// The speech synthesis connection latency in milliseconds. Read-only, available on final speech synthesis results. + /// This measures the latency between when the synthesis is started to be processed, and the moment the HTTP/WebSocket connection is established. + /// Added in version 1.26.0. + /// + SpeechServiceResponse_SynthesisConnectionLatencyMs = 5013, + + /// + /// The speech synthesis network latency in milliseconds. Read-only, available on final speech synthesis results. + /// This measures the network round trip time. + /// Added in version 1.26.0. + /// + SpeechServiceResponse_SynthesisNetworkLatencyMs = 5014, + + /// + /// The speech synthesis service latency in milliseconds. Read-only, available on final speech synthesis results. + /// This measures the service processing time to synthesize the first byte of audio. + /// Added in version 1.26.0. + /// + SpeechServiceResponse_SynthesisServiceLatencyMs = 5015, + + /// + /// Indicates which backend the synthesis is finished by. Read-only, available on speech synthesis results, except for the result in SynthesisStarted event + /// Added in version 1.17.0. + /// + SpeechServiceResponse_SynthesisBackend = 5020, + + /// + /// Determines if intermediate results contain speaker identification. + /// + /// + /// + /// Allowed values are "true" or "false". If set to "true", the intermediate results will contain speaker identification. + /// The default value if unset or set to an invalid value is "false". + /// + /// + /// This is currently only supported for scenarios using the + /// + /// + /// Adding in version 1.40. + /// + /// + SpeechServiceResponse_DiarizeIntermediateResults = 5025, + + /// + /// The cancellation reason. Currently unused. + /// + CancellationDetails_Reason = 6000, + + /// + /// The cancellation text. Currently unused. + /// + CancellationDetails_ReasonText = 6001, + + /// + /// The cancellation detailed text. Currently unused. + /// + CancellationDetails_ReasonDetailedText = 6002, + + /// + /// The Language Understanding Service response output (in JSON format). Available via . + /// + LanguageUnderstandingServiceResponse_JsonResult = 7000, + + /// + /// The device name for audio capture. Under normal circumstances, you shouldn't have to + /// use this property directly. + /// Instead, use . + /// NOTE: This property id was added in version 1.3.0. + /// + AudioConfig_DeviceNameForCapture = 8000, + + /// + /// The number of channels for audio capture. Internal use only. + /// NOTE: This property id was added in version 1.3.0. + /// + AudioConfig_NumberOfChannelsForCapture = 8001, + + /// + /// The sample rate (in Hz) for audio capture. Internal use only. + /// NOTE: This property id was added in version 1.3.0. + /// + AudioConfig_SampleRateForCapture = 8002, + + /// + /// The number of bits of each sample for audio capture. Internal use only. + /// NOTE: This property id was added in version 1.3.0. + /// + AudioConfig_BitsPerSampleForCapture = 8003, + + /// + /// The audio source. Allowed values are "Microphones", "File", and "Stream". + /// Added in version 1.3.0. + /// + AudioConfig_AudioSource = 8004, + + /// + /// The device name for audio render. Under normal circumstances, you shouldn't have to + /// use this property directly. + /// Instead, use . + /// Added in version 1.14.0 + /// + AudioConfig_DeviceNameForRender = 8005, + + /// + /// Playback buffer length in milliseconds, default is 50 milliseconds. + /// + AudioConfig_PlaybackBufferLengthInMs = 8006, + + /// + /// Audio processing options in JSON format. + /// + AudioConfig_AudioProcessingOptions = 8007, + + /// + /// The file name to write logs. + /// Added in version 1.4.0. + /// + Speech_LogFilename = 9001, + + /// + /// A duration of detected silence, measured in milliseconds, after which speech-to-text will determine a spoken + /// phrase has ended and generate a final Recognized result. Configuring this timeout may be helpful in situations + /// where spoken input is significantly faster or slower than usual and default segmentation behavior consistently + /// yields results that are too long or too short. Segmentation timeout values that are inappropriately high or low + /// can negatively affect speech-to-text accuracy; this property should be carefully configured and the resulting + /// behavior should be thoroughly validated as intended. + /// + /// For more information about timeout configuration that includes discussion of default behaviors, please visit + /// https://aka.ms/csspeech/timeouts. + /// + Speech_SegmentationSilenceTimeoutMs = 9002, + + /// + /// The maximum length of a spoken phrase when using the "Time" segmentation strategy. + /// As the length of a spoken phrase approaches this value, the will begin being reduced until either the phrase silence timeout is hit or the phrase reaches the maximum length. + /// + Speech_SegmentationMaximumTimeMs = 9003, + + /// + /// The strategy used to determine when a spoken phrase has ended and a final Recognized result should be generated. + /// Allowed values are "Default", "Time", and "Semantic". + /// + /// + /// Valid values are: + /// + /// + /// Default + /// Use the default strategy and settings as determined by the Speech Service. Use in most situations. + /// + /// + /// Time + /// Uses a time based strategy where the amount of silence between speech is used to determine when to generate a final result. + /// + /// + /// Semantic + /// Uses an AI model to deterine the end of a spoken phrase based on the content of the phrase. + /// + /// + /// + /// When using the time strategy, the property can be used to adjust the amount of silence needed to determine the end of a spoken phrase, + /// and the property can be used to adjust the maximum length of a spoken phrase. + /// + /// + /// The semantic strategy has no control properties available. + /// + /// + Speech_SegmentationStrategy = 9004, + + /// + /// Identifier used to connect to the backend service. + /// Added in version 1.5.0. + /// + Conversation_ApplicationId = 10000, + + /// + /// Type of dialog backend to connect to. + /// Added in version 1.7.0. + /// + Conversation_DialogType = 10001, + + /// + /// Silence timeout for listening + /// Added in version 1.5.0. + /// + Conversation_Initial_Silence_Timeout = 10002, + + /// + /// From id to be used on speech recognition activities + /// Added in version 1.5.0. + /// + Conversation_From_Id = 10003, + + /// + /// ConversationId for the session. + /// Added in version 1.8.0. + /// + Conversation_Conversation_Id = 10004, + + /// + /// Comma separated list of custom voice deployment ids. + /// Added in version 1.8.0. + /// + Conversation_Custom_Voice_Deployment_Ids = 10005, + + /// + /// Speech activity template, stamp properties in the template on the activity generated by the service for speech. + /// Added in version 1.10.0. + /// + Conversation_Speech_Activity_Template = 10006, + + /// + /// Your participant identifier in the current conversation. + /// Added in version 1.13.0 + /// + Conversation_ParticipantId = 10007, + + // If specified as true, request that the service send MessageStatus payloads via the ActivityReceived event + // handler. These messages communicate the outcome of ITurnContext resolution from the dialog system. + // Added in version 1.14.0. + Conversation_Request_Bot_Status_Messages = 10008, + + // Additional identifying information, such as a Direct Line token, used to authenticate with the backend service. + // Added in version 1.16.0. + Conversation_Connection_Id = 10009, + + /// + /// The time stamp associated to data buffer written by client when using Pull/Push audio input streams. + /// The time stamp is a 64-bit value with a resolution of 90 kHz. It is the same as the presentation timestamp in an MPEG transport stream. See https://en.wikipedia.org/wiki/Presentation_timestamp + /// Added in version 1.5.0. + /// + DataBuffer_TimeStamp = 11001, + + /// + /// The user id associated to data buffer written by client when using Pull/Push audio input streams. + /// Added in version 1.5.0. + /// + DataBuffer_UserId = 11002, + + /// + /// The reference text of the audio for pronunciation evaluation. + /// For this and the following pronunciation assessment parameters, see the table + /// [Pronunciation assessment parameters](/azure/cognitive-services/speech-service/rest-speech-to-text-short#pronunciation-assessment-parameters). + /// Under normal circumstances, you shouldn't have to use this property directly. + /// Instead, use or . + /// Added in version 1.14.0 + /// + PronunciationAssessment_ReferenceText = 12001, + + /// + /// The point system for pronunciation score calibration (FivePoint or HundredMark). + /// Under normal circumstances, you shouldn't have to use this property directly. + /// Instead, use . + /// Added in version 1.14.0 + /// + PronunciationAssessment_GradingSystem = 12002, + + /// + /// The pronunciation evaluation granularity (Phoneme, Word, or FullText). + /// Under normal circumstances, you shouldn't have to use this property directly. + /// Instead, use . + /// Added in version 1.14.0 + /// + PronunciationAssessment_Granularity = 12003, + + /// + /// Defines if enable miscue calculation. + /// With this enabled, the pronounced words will be compared to the reference text, + /// and will be marked with omission/insertion based on the comparison. The default setting is False. + /// Under normal circumstances, you shouldn't have to use this property directly. + /// Instead, use . + /// Added in version 1.14.0 + /// + PronunciationAssessment_EnableMiscue = 12005, + + /// + /// The pronunciation evaluation phoneme alphabet. The valid values are "SAPI" (default) and "IPA" + /// Under normal circumstances, you shouldn't have to use this property directly. + /// Instead, use . + /// Added in version 1.20.0 + /// + PronunciationAssessment_PhonemeAlphabet = 12006, + + /// + /// The pronunciation evaluation nbest phoneme count. + /// Under normal circumstances, you shouldn't have to use this property directly. + /// Instead, use . + /// Added in version 1.20.0 + /// + PronunciationAssessment_NBestPhonemeCount = 12007, + + /// + /// Whether to enable prosody assessment. + /// Under normal circumstances, you shouldn't have to use this property directly. + /// Instead, use . + /// Added in version 1.33.0 + /// + PronunciationAssessment_EnableProsodyAssessment = 12008, + + /// + /// The json string of pronunciation assessment parameters + /// Under normal circumstances, you shouldn't have to use this property directly. + /// Instead, use . + /// Added in version 1.14.0 + /// + PronunciationAssessment_Json = 12009, + + /// + /// Pronunciation assessment parameters. + /// This property is intended to be read-only. The SDK is using it internally. + /// Added in version 1.14.0 + /// + PronunciationAssessment_Params = 12010, + + /// + /// The content topic of the pronunciation assessment. + /// Under normal circumstances, you shouldn't have to use this property directly. + /// Instead, use . + /// Added in version 1.33.0 + /// + PronunciationAssessment_ContentTopic = 12020, + + /// + /// Speaker Recognition backend API version. + /// This property is added to allow testing and use of previous versions of Speaker Recognition APIs, where applicable. + /// Added in version 1.18.0 + /// + SpeakerRecognition_Api_Version = 13001, + + /// + /// The name of a model to be used for speech translation. + /// Do not use this property directly. + /// Currently this is only valid when EmbeddedSpeechConfig is used. + /// + SpeechTranslation_ModelName = 13100, + + /// + /// This property is deprecated. + /// + SpeechTranslation_ModelKey = 13101, + + /// + /// The name of a model to be used for keyword recognition. + /// Do not use this property directly. + /// Currently this is only valid when EmbeddedSpeechConfig is used. + /// + KeywordRecognition_ModelName = 13200, + + /// + /// This property is deprecated. + /// + KeywordRecognition_ModelKey = 13201, + + /// + /// Enable the collection of embedded speech performance metrics which can + /// be used to evaluate the capability of a device to use embedded speech. + /// The collected data is included in results from specific scenarios like + /// speech recognition. + /// The default setting is "false". Note that metrics may not be available + /// from all embedded speech scenarios. + /// + EmbeddedSpeech_EnablePerformanceMetrics = 13300, + + /// + /// The pitch of the synthesized speech. + /// + SpeechSynthesisRequest_Pitch = 14001, + + /// + /// The rate of the synthesized speech. + /// + SpeechSynthesisRequest_Rate = 14002, + + /// + /// The volume of the synthesized speech. + /// + SpeechSynthesisRequest_Volume = 14003, +}; + +/// +/// Output format. +/// +enum class OutputFormat +{ + Simple = 0, + Detailed = 1 +}; + +/// +/// Removes profanity (swearing), or replaces letters of profane words with stars. +/// Added in version 1.5.0. +/// +enum class ProfanityOption +{ + /// + /// Replaces letters in profane words with star characters. + /// + Masked = 0, + /// + /// Removes profane words. + /// + Removed = 1, + /// + /// Does nothing to profane words. + /// + Raw = 2 +}; + +/// +/// Specifies the possible reasons a recognition result might be generated. +/// +enum class ResultReason +{ + /// + /// Indicates speech could not be recognized. More details can be found in the NoMatchDetails object. + /// + NoMatch = 0, + + /// + /// Indicates that the recognition was canceled. More details can be found using the CancellationDetails object. + /// + Canceled = 1, + + /// + /// Indicates the speech result contains hypothesis text. + /// + RecognizingSpeech = 2, + + /// + /// Indicates the speech result contains final text that has been recognized. + /// Speech Recognition is now complete for this phrase. + /// + RecognizedSpeech = 3, + + /// + /// Indicates the intent result contains hypothesis text and intent. + /// + RecognizingIntent = 4, + + /// + /// Indicates the intent result contains final text and intent. + /// Speech Recognition and Intent determination are now complete for this phrase. + /// + RecognizedIntent = 5, + + /// + /// Indicates the translation result contains hypothesis text and its translation(s). + /// + TranslatingSpeech = 6, + + /// + /// Indicates the translation result contains final text and corresponding translation(s). + /// Speech Recognition and Translation are now complete for this phrase. + /// + TranslatedSpeech = 7, + + /// + /// Indicates the synthesized audio result contains a non-zero amount of audio data + /// + SynthesizingAudio = 8, + + /// + /// Indicates the synthesized audio is now complete for this phrase. + /// + SynthesizingAudioCompleted = 9, + + /// + /// Indicates the speech result contains (unverified) keyword text. + /// Added in version 1.3.0 + /// + RecognizingKeyword = 10, + + /// + /// Indicates that keyword recognition completed recognizing the given keyword. + /// Added in version 1.3.0 + /// + RecognizedKeyword = 11, + + /// + /// Indicates the speech synthesis is now started + /// Added in version 1.4.0 + /// + SynthesizingAudioStarted = 12, + + /// + /// Indicates the transcription result contains hypothesis text and its translation(s) for + /// other participants in the conversation. + /// Added in version 1.8.0 + /// + TranslatingParticipantSpeech = 13, + + /// + /// Indicates the transcription result contains final text and corresponding translation(s) + /// for other participants in the conversation. Speech Recognition and Translation are now + /// complete for this phrase. + /// Added in version 1.8.0 + /// + TranslatedParticipantSpeech = 14, + + /// + /// Indicates the transcription result contains the instant message and corresponding + /// translation(s). + /// Added in version 1.8.0 + /// + TranslatedInstantMessage = 15, + + /// + /// Indicates the transcription result contains the instant message for other participants + /// in the conversation and corresponding translation(s). + /// Added in version 1.8.0 + /// + TranslatedParticipantInstantMessage = 16, + + /// + /// Indicates the voice profile is being enrolling and customers need to send more audio to create a voice profile. + /// Added in version 1.12.0 + /// + EnrollingVoiceProfile = 17, + + /// + /// The voice profile has been enrolled. + /// Added in version 1.12.0 + /// + EnrolledVoiceProfile = 18, + + /// + /// Indicates successful identification of some speakers. + /// Added in version 1.12.0 + /// + RecognizedSpeakers = 19, + + /// + /// Indicates successfully verified one speaker. + /// Added in version 1.12.0 + /// + RecognizedSpeaker = 20, + + /// + /// Indicates a voice profile has been reset successfully. + /// Added in version 1.12.0 + /// + ResetVoiceProfile = 21, + + /// + /// Indicates a voice profile has been deleted successfully. + /// Added in version 1.12.0 + /// + DeletedVoiceProfile = 22, + + /// + /// Indicates the voices list has been retrieved successfully. + /// Added in version 1.16.0 + /// + VoicesListRetrieved = 23 +}; + +/// +/// Defines the possible reasons a recognition result might be canceled. +/// +enum class CancellationReason +{ + /// + /// Indicates that an error occurred during speech recognition. + /// + Error = 1, + + /// + /// Indicates that the end of the audio stream was reached. + /// + EndOfStream = 2, + + /// + /// Indicates that request was cancelled by the user. + /// Added in version 1.14.0 + /// + CancelledByUser = 3, +}; + +/// +/// Defines error code in case that CancellationReason is Error. +/// Added in version 1.1.0. +/// +enum class CancellationErrorCode +{ + /// + /// No error. + /// If CancellationReason is EndOfStream, CancellationErrorCode + /// is set to NoError. + /// + NoError = 0, + + /// + /// Indicates an authentication error. + /// An authentication error occurs if subscription key or authorization token is invalid, expired, + /// or does not match the region being used. + /// + AuthenticationFailure = 1, + + /// + /// Indicates that one or more recognition parameters are invalid or the audio format is not supported. + /// + BadRequest = 2, + + /// + /// Indicates that the number of parallel requests exceeded the number of allowed concurrent transcriptions for the subscription. + /// + TooManyRequests = 3, + + /// + /// Indicates that the free subscription used by the request ran out of quota. + /// + Forbidden = 4, + + /// + /// Indicates a connection error. + /// + ConnectionFailure = 5, + + /// + /// Indicates a time-out error when waiting for response from service. + /// + ServiceTimeout = 6, + + /// + /// Indicates that an error is returned by the service. + /// + ServiceError = 7, + + /// + /// Indicates that the service is currently unavailable. + /// + ServiceUnavailable = 8, + + /// + /// Indicates an unexpected runtime error. + /// + RuntimeError = 9, + + /// + /// Indicates the Speech Service is temporarily requesting a reconnect to a different endpoint. + /// + /// Used internally + ServiceRedirectTemporary = 10, + + /// + /// Indicates the Speech Service is permanently requesting a reconnect to a different endpoint. + /// + /// Used internally + ServiceRedirectPermanent = 11, + + /// + /// Indicates the embedded speech (SR or TTS) model is not available or corrupted. + /// + EmbeddedModelError = 12, +}; + +/// +/// Defines the possible reasons a recognition result might not be recognized. +/// +enum class NoMatchReason +{ + /// + /// Indicates that speech was detected, but not recognized. + /// + NotRecognized = 1, + + /// + /// Indicates that the start of the audio stream contained only silence, and the service timed out waiting for speech. + /// + InitialSilenceTimeout = 2, + + /// + /// Indicates that the start of the audio stream contained only noise, and the service timed out waiting for speech. + /// + InitialBabbleTimeout = 3, + + /// + /// Indicates that the spotted keyword has been rejected by the keyword verification service. + /// Added in version 1.5.0. + /// + KeywordNotRecognized = 4, + + /// + /// Indicates that the audio stream contained only silence after the last recognized phrase. + /// + EndSilenceTimeout = 5 +}; + +/// +/// Defines the possible types for an activity json value. +/// Added in version 1.5.0 +/// +enum class ActivityJSONType : int +{ + Null = 0, + Object = 1, + Array = 2, + String = 3, + Double = 4, + UInt = 5, + Int = 6, + Boolean = 7 +}; + + +/// +/// Defines the possible speech synthesis output audio formats. +/// Updated in version 1.19.0 +/// +enum class SpeechSynthesisOutputFormat +{ + /// + /// raw-8khz-8bit-mono-mulaw + /// + Raw8Khz8BitMonoMULaw = 1, + + /// + /// riff-16khz-16kbps-mono-siren + /// Unsupported by the service. Do not use this value. + /// + Riff16Khz16KbpsMonoSiren = 2, + + /// + /// audio-16khz-16kbps-mono-siren + /// Unsupported by the service. Do not use this value. + /// + Audio16Khz16KbpsMonoSiren = 3, + + /// + /// audio-16khz-32kbitrate-mono-mp3 + /// + Audio16Khz32KBitRateMonoMp3 = 4, + + /// + /// audio-16khz-128kbitrate-mono-mp3 + /// + Audio16Khz128KBitRateMonoMp3 = 5, + + /// + /// audio-16khz-64kbitrate-mono-mp3 + /// + Audio16Khz64KBitRateMonoMp3 = 6, + + /// + /// audio-24khz-48kbitrate-mono-mp3 + /// + Audio24Khz48KBitRateMonoMp3 =7, + + /// + /// audio-24khz-96kbitrate-mono-mp3 + /// + Audio24Khz96KBitRateMonoMp3 = 8, + + /// + /// audio-24khz-160kbitrate-mono-mp3 + /// + Audio24Khz160KBitRateMonoMp3 = 9, + + /// + /// raw-16khz-16bit-mono-truesilk + /// + Raw16Khz16BitMonoTrueSilk = 10, + + /// + /// riff-16khz-16bit-mono-pcm + /// + Riff16Khz16BitMonoPcm = 11, + + /// + /// riff-8khz-16bit-mono-pcm + /// + Riff8Khz16BitMonoPcm = 12, + + /// + /// riff-24khz-16bit-mono-pcm + /// + Riff24Khz16BitMonoPcm = 13, + + /// + /// riff-8khz-8bit-mono-mulaw + /// + Riff8Khz8BitMonoMULaw = 14, + + /// + /// raw-16khz-16bit-mono-pcm + /// + Raw16Khz16BitMonoPcm = 15, + + /// + /// raw-24khz-16bit-mono-pcm + /// + Raw24Khz16BitMonoPcm = 16, + + /// + /// raw-8khz-16bit-mono-pcm + /// + Raw8Khz16BitMonoPcm = 17, + + /// + /// ogg-16khz-16bit-mono-opus + /// + Ogg16Khz16BitMonoOpus = 18, + + /// + /// ogg-24khz-16bit-mono-opus + /// + Ogg24Khz16BitMonoOpus = 19, + + /// + /// raw-48khz-16bit-mono-pcm + /// + Raw48Khz16BitMonoPcm = 20, + + /// + /// riff-48khz-16bit-mono-pcm + /// + Riff48Khz16BitMonoPcm = 21, + + /// + /// audio-48khz-96kbitrate-mono-mp3 + /// + Audio48Khz96KBitRateMonoMp3 = 22, + + /// + /// audio-48khz-192kbitrate-mono-mp3 + /// + Audio48Khz192KBitRateMonoMp3 = 23, + + /// + /// ogg-48khz-16bit-mono-opus + /// Added in version 1.16.0 + /// + Ogg48Khz16BitMonoOpus = 24, + + /// + /// webm-16khz-16bit-mono-opus + /// Added in version 1.16.0 + /// + Webm16Khz16BitMonoOpus = 25, + + /// + /// webm-24khz-16bit-mono-opus + /// Added in version 1.16.0 + /// + Webm24Khz16BitMonoOpus = 26, + + /// + /// raw-24khz-16bit-mono-truesilk + /// Added in version 1.17.0 + /// + Raw24Khz16BitMonoTrueSilk = 27, + + /// + /// raw-8khz-8bit-mono-alaw + /// Added in version 1.17.0 + /// + Raw8Khz8BitMonoALaw = 28, + + /// + /// riff-8khz-8bit-mono-alaw + /// Added in version 1.17.0 + /// + Riff8Khz8BitMonoALaw = 29, + + /// + /// webm-24khz-16bit-24kbps-mono-opus + /// Audio compressed by OPUS codec in a WebM container, with bitrate of 24kbps, optimized for IoT scenario. + /// (Added in 1.19.0) + /// + Webm24Khz16Bit24KbpsMonoOpus = 30, + + /// + /// audio-16khz-16bit-32kbps-mono-opus + /// Audio compressed by OPUS codec without container, with bitrate of 32kbps. + /// (Added in 1.20.0) + /// + Audio16Khz16Bit32KbpsMonoOpus = 31, + + /// + /// audio-24khz-16bit-48kbps-mono-opus + /// Audio compressed by OPUS codec without container, with bitrate of 48kbps. + /// (Added in 1.20.0) + /// + Audio24Khz16Bit48KbpsMonoOpus = 32, + + /// + /// audio-24khz-16bit-24kbps-mono-opus + /// Audio compressed by OPUS codec without container, with bitrate of 24kbps. + /// (Added in 1.20.0) + /// + Audio24Khz16Bit24KbpsMonoOpus = 33, + + /// + /// raw-22050hz-16bit-mono-pcm + /// Raw PCM audio at 22050Hz sampling rate and 16-bit depth. + /// (Added in 1.22.0) + /// + Raw22050Hz16BitMonoPcm = 34, + + /// + /// riff-22050hz-16bit-mono-pcm + /// PCM audio at 22050Hz sampling rate and 16-bit depth, with RIFF header. + /// (Added in 1.22.0) + /// + Riff22050Hz16BitMonoPcm = 35, + + /// + /// raw-44100hz-16bit-mono-pcm + /// Raw PCM audio at 44100Hz sampling rate and 16-bit depth. + /// (Added in 1.22.0) + /// + Raw44100Hz16BitMonoPcm = 36, + + /// + /// riff-44100hz-16bit-mono-pcm + /// PCM audio at 44100Hz sampling rate and 16-bit depth, with RIFF header. + /// (Added in 1.22.0) + /// + Riff44100Hz16BitMonoPcm = 37, + + /// + /// amr-wb-16000hz + /// AMR-WB audio at 16kHz sampling rate. + /// (Added in 1.24.0) + /// + AmrWb16000Hz = 38, + + /// + /// g722-16khz-64kbps + /// G.722 audio at 16kHz sampling rate and 64kbps bitrate. + /// (Added in 1.38.0) + /// + G72216Khz64Kbps = 39 +}; + +/// +/// Defines the possible status of audio data stream. +/// Added in version 1.4.0 +/// +enum class StreamStatus +{ + /// + /// The audio data stream status is unknown + /// + Unknown = 0, + + /// + /// The audio data stream contains no data + /// + NoData = 1, + + /// + /// The audio data stream contains partial data of a speak request + /// + PartialData = 2, + + /// + /// The audio data stream contains all data of a speak request + /// + AllData = 3, + + /// + /// The audio data stream was canceled + /// + Canceled = 4 +}; + +/// +/// Defines channels used to pass property settings to service. +/// Added in version 1.5.0. +/// +enum class ServicePropertyChannel +{ + /// + /// Uses URI query parameter to pass property settings to service. + /// + UriQueryParameter = 0, + + /// + /// Uses HttpHeader to set a key/value in a HTTP header. + /// + HttpHeader = 1 +}; + +namespace Transcription +{ + /// + /// Why the participant changed event was raised + /// Added in version 1.8.0 + /// + enum class ParticipantChangedReason + { + /// + /// Participant has joined the conversation + /// + JoinedConversation = 0, + + /// + /// Participant has left the conversation. This could be voluntary, or involuntary + /// (e.g. they are experiencing networking issues) + /// + LeftConversation = 1, + + /// + /// The participants' state has changed (e.g. they became muted, changed their nickname) + /// + Updated = 2 + }; +} + +namespace Intent +{ + /// + /// Used to define the type of entity used for intent recognition. + /// + enum class EntityType + { + /// + /// This will match any text that fills the slot. + /// + Any = 0, + /// + /// This will match text that is contained within the list or any text if the mode is set to "fuzzy". + /// + List = 1, + /// + /// This will match cardinal and ordinal integers. + /// + PrebuiltInteger = 2 + }; + + /// + /// Used to define the type of entity used for intent recognition. + /// + enum class EntityMatchMode + { + /// + /// This is the basic or default mode of matching based on the EntityType + /// + Basic = 0, + /// + /// This will match only exact matches within the entities phrases. + /// + Strict = 1, + /// + /// This will match text within the slot the entity is in, but not require anything from that text. + /// + Fuzzy = 2 + }; + + /// + /// Used to define the greediness of the entity. + /// + enum class EntityGreed + { + /// + /// Lazy will match as little as possible. + /// + Lazy = 0, + /// + /// Greedy will match as much as possible. + /// + Greedy = 1, + }; +} +/// +/// Defines voice profile types +/// +enum class VoiceProfileType +{ + /// + /// Text independent speaker identification. + /// + TextIndependentIdentification = 1, + + /// + /// Text dependent speaker verification. + /// + TextDependentVerification = 2, + + /// + /// Text independent verification. + /// + TextIndependentVerification = 3 +}; + +/// +/// Defines the scope that a Recognition Factor is applied to. +/// +enum class RecognitionFactorScope +{ + /// + /// A Recognition Factor will apply to grammars that can be referenced as individual partial phrases. + /// + /// + /// Currently only applies to PhraseListGrammars + /// + PartialPhrase = 1, +}; + +/// +/// Defines the point system for pronunciation score calibration; default value is FivePoint. +/// Added in version 1.14.0 +/// +enum class PronunciationAssessmentGradingSystem +{ + /// + /// Five point calibration + /// + FivePoint = 1, + + /// + /// Hundred mark + /// + HundredMark = 2 +}; + +/// +/// Defines the pronunciation evaluation granularity; default value is Phoneme. +/// Added in version 1.14.0 +/// +enum class PronunciationAssessmentGranularity +{ + /// + /// Shows the score on the full text, word and phoneme level + /// + Phoneme = 1, + + /// + /// Shows the score on the full text and word level + /// + Word = 2, + + /// + /// Shows the score on the full text level only + /// + FullText = 3 +}; + +/// +/// Defines the type of synthesis voices +/// Added in version 1.16.0 +/// +enum class SynthesisVoiceType +{ + /// + /// Online neural voice + /// + OnlineNeural = 1, + + /// + /// Online standard voice + /// + OnlineStandard = 2, + + /// + /// Offline neural voice + /// + OfflineNeural = 3, + + /// + /// Offline standard voice + /// + OfflineStandard = 4 +}; + +/// +/// Defines the gender of synthesis voices +/// Added in version 1.17.0 +/// +enum class SynthesisVoiceGender +{ + /// + /// Gender unknown. + /// + Unknown = 0, + + /// + /// Female voice + /// + Female = 1, + + /// + /// Male voice + /// + Male = 2 +}; + +/// +/// Defines the boundary type of speech synthesis boundary event +/// Added in version 1.21.0 +/// +enum class SpeechSynthesisBoundaryType +{ + /// + /// Word boundary + /// + Word = 0, + + /// + /// Punctuation boundary + /// + Punctuation = 1, + + /// + /// Sentence boundary + /// + Sentence = 2 +}; + +/// +/// The strategy used to determine when a spoken phrase has ended and a final Recognized result should be generated. +/// Allowed values are "Default", "Time", and "Semantic". +/// +enum class SegmentationStrategy +{ + /// + /// Use the default strategy and settings as determined by the Speech Service. Use in most situations. + /// + Default = 0, + + /// + /// Uses a time based strategy where the amount of silence between speech is used to determine when to generate a final result. + /// + /// + /// When using the time strategy, the property can be used to adjust the amount of silence needed to determine the end of a spoken phrase, + /// and the property can be used to adjust the maximum length of a spoken phrase. + /// + Time = 1, + + /// + /// Uses an AI model to deterine the end of a spoken phrase based on the content of the phrase. + /// + /// + /// The semantic strategy has no control properties available. + /// + Semantic = 2 +}; + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_event_logger.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_event_logger.h new file mode 100644 index 0000000..4e67cb1 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_event_logger.h @@ -0,0 +1,108 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Diagnostics { +namespace Logging { + +/// +/// Class with static methods to control callback-based SDK logging. +/// Turning on logging while running your Speech SDK scenario provides +/// detailed information from the SDK's core native components. If you +/// report an issue to Microsoft, you may be asked to provide logs to help +/// Microsoft diagnose the issue. Your application should not take dependency +/// on particular log strings, as they may change from one SDK release to another +/// without notice. +/// Use EventLogger when you want to get access to new log strings as soon +/// as they are available, and you need to further process them. For example, +/// integrating Speech SDK logs with your existing logging collection system. +/// Added in version 1.20.0 +/// +/// Event logging is a process wide construct. That means that if (for example) +/// you have multiple speech recognizer objects running in parallel, you can only register +/// one callback function to receive interleaved logs from all recognizers. You cannot register +/// a separate callback for each recognizer. +class EventLogger +{ +public: + using CallbackFunction_Type = ::std::function; + + /// + /// Register a callback function that will be invoked for each new log messages. + /// + /// callback function to call. Set a nullptr value + /// to stop the Event Logger. + /// You can only register one callback function. This call will happen on a working thread of the SDK, + /// so the log string should be copied somewhere for further processing by another thread, and the function should return immediately. + /// No heavy processing or network calls should be done in this callback function. + static void SetCallback(CallbackFunction_Type callback = nullptr) + { + AZAC_THROW_ON_FAIL(diagnostics_logmessage_set_callback(nullptr == callback ? nullptr : LineLogged)); + + SetOrGet(true, callback); + } + + /// + /// Sets or clears filters for callbacks. + /// Once filters are set, the callback will be invoked only if the log string + /// contains at least one of the strings specified by the filters. The match is case sensitive. + /// + /// Optional. Filters to use, or an empty list to clear previously set filters + static void SetFilters(std::initializer_list filters = {}) + { + std::string str = ""; + + if (filters.size() > 0) + { + std::ostringstream filtersCollapsed; + std::copy(filters.begin(), filters.end(), std::ostream_iterator(filtersCollapsed, ";")); + str = filtersCollapsed.str(); + } + + AZAC_THROW_ON_FAIL(diagnostics_logmessage_set_filters(str.c_str())); + } + + /// + /// Sets the level of the messages to be captured by the logger + /// + /// Maximum level of detail to be captured by the logger. + static void SetLevel(Level level) + { + const auto levelStr = Details::LevelToString(level); + diagnostics_set_log_level("event", levelStr); + } + +private: + static CallbackFunction_Type SetOrGet(bool set, CallbackFunction_Type callback) + { + static CallbackFunction_Type staticCallback = nullptr; + if (set) + { + staticCallback = callback; + } + return staticCallback; + } + + static void LineLogged(const char* line) + { + auto callback = SetOrGet(false, nullptr); + if (nullptr != callback) + { + callback(line); + } + } +}; +}}}}} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_eventargs.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_eventargs.h new file mode 100644 index 0000000..8142268 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_eventargs.h @@ -0,0 +1,47 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_eventargs.h: Public API declarations for EventArgs C++ base class +// + +#pragma once +#include +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + + +/// +/// Base class for event arguments. +/// +class EventArgs +{ +public: + + /// + /// Destructor. + /// + virtual ~EventArgs() {} + +protected: + + /*! \cond PROTECTED */ + + /// + /// Constructor. + /// + EventArgs() {}; + + /*! \endcond */ + +private: + + DISABLE_COPY_AND_MOVE(EventArgs); +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_eventsignal.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_eventsignal.h new file mode 100644 index 0000000..5c544a6 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_eventsignal.h @@ -0,0 +1,202 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_eventsignal.h: Public API declarations for the EventSignal class. This derives from +// EventSignalBase and uses runtime type information (RTTI) to facilitate management and disconnection of handlers +// without explicit callback token management. +// + +#pragma once +#include +#include +#include +#include + +#include + +// TODO: TFS#3671067 - Vision: Consider moving majority of EventSignal to AI::Core::Details namespace, and refactoring Vision::Core::Events to inherit, and relay to private base + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Clients can connect to the event signal to receive events, or disconnect from the event signal to stop receiving events. +/// +/// +/// At construction time, connect and disconnect callbacks can be provided that are called when +/// the number of connected clients changes from zero to one or one to zero, respectively. +/// +// +template +class EventSignal : public EventSignalBase +{ +public: + /// + /// Callback type that is used for signalling the event to connected clients. + /// + using CallbackFunction = std::function; + + /// + /// A monotonically increasing token used for registration, tracking, and unregistration of callbacks. + /// + using CallbackToken = uint32_t; + + /// + /// Type for callbacks used when any client connects to the signal (the number of connected clients changes from zero to one) or + /// the last client disconnects from the signal (the number of connected clients changes from one to zero). + /// + using NotifyCallback_Type = std::function&)>; + + /// + /// Constructs an event signal with empty register and disconnect callbacks. + /// + EventSignal() : EventSignal(nullptr) + { + } + + /// + /// Constructor. + /// + /// Callback to invoke if the number of connected clients changes from zero to one, or one to zero + EventSignal(NotifyCallback_Type connectedAndDisconnected) + : EventSignal(connectedAndDisconnected, connectedAndDisconnected) + { + } + + /// + /// Constructor. + /// + /// Callback to invoke if the number of connected clients changes from zero to one. + /// Callback to invoke if the number of connected clients changes from one to zero. + EventSignal(NotifyCallback_Type connected, NotifyCallback_Type disconnected) + : EventSignalBase() + , m_firstConnectedCallback(connected) + , m_lastDisconnectedCallback(disconnected) + { + } + + /// + /// Addition assignment operator overload. + /// Connects the provided callback to the event signal, see also . + /// + /// Callback to connect. + /// Event signal reference. + EventSignal& operator+=(CallbackFunction callback) + { + Connect(callback); + return *this; + } + + /// + /// Subtraction assignment operator overload. + /// Disconnects the provided callback from the event signal, see also . + /// + /// Callback to disconnect. + /// Event signal reference. + EventSignal& operator-=(CallbackFunction callback) + { + Disconnect(callback); + return *this; + } + + /// + /// Connects given callback function to the event signal, to be invoked when the event is signalled. + /// + /// + /// When the number of connected clients changes from zero to one, the connect callback will be called, if provided. + /// + /// Callback to connect. + void Connect(CallbackFunction callback) + { + std::unique_lock lock(m_mutex); + + auto shouldFireFirstConnected = m_callbacks.empty() && m_firstConnectedCallback != nullptr; + + (void)EventSignalBase::RegisterCallback(callback); + + lock.unlock(); + + if (shouldFireFirstConnected) + { + m_firstConnectedCallback(*this); + } + } + +#ifndef AZAC_CONFIG_CXX_NO_RTTI + /// + /// Disconnects given callback. + /// + /// + /// When the number of connected clients changes from one to zero, the disconnect callback will be called, if provided. + /// + /// Callback function. + void Disconnect(CallbackFunction callback) + { + std::unique_lock lock(m_mutex); + + auto itMatchingCallback = std::find_if( + m_callbacks.begin(), + m_callbacks.end(), + [&](const std::pair& item) + { + return callback.target_type() == item.second.target_type(); + }); + + auto removeHappened = EventSignal::UnregisterCallback(itMatchingCallback->first); + lock.unlock(); + if (removeHappened && m_callbacks.empty() && m_lastDisconnectedCallback != nullptr) + { + m_lastDisconnectedCallback(*this); + } + } +#else + void Disconnect(CallbackFunction) + { + // Callback disconnection without a stored token requires runtime type information. + // To remove callbacks with RTTI disabled, use UnregisterCallback(token). + SPX_THROW_HR(SPXERR_NOT_IMPL); + } +#endif + + /// + /// Disconnects all registered callbacks. + /// + void DisconnectAll() + { + std::unique_lock lock(m_mutex); + auto shouldFireLastDisconnected = !m_callbacks.empty() && m_lastDisconnectedCallback != nullptr; + + EventSignal::UnregisterAllCallbacks(); + + lock.unlock(); + + if (shouldFireLastDisconnected) + { + m_lastDisconnectedCallback(*this); + } + } + + /// + /// Signals the event with given arguments to all connected callbacks. + /// + /// Event arguments to signal. + void Signal(T t) + { + EventSignalBase::Signal(t); + } + +private: + using EventSignalBase::m_mutex; + using EventSignalBase::m_callbacks; + + NotifyCallback_Type m_firstConnectedCallback; + NotifyCallback_Type m_lastDisconnectedCallback; + + EventSignal(const EventSignal&) = delete; + EventSignal(const EventSignal&&) = delete; + EventSignal& operator=(const EventSignal&) = delete; +}; + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_eventsignalbase.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_eventsignalbase.h new file mode 100644 index 0000000..0a0de7e --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_eventsignalbase.h @@ -0,0 +1,166 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_eventsignalbase.h: Public API declarations for EventSignalBase C++ template class +// + +#pragma once +#include +#include +#include +#include +#include + +// TODO: TFS#3671067 - Vision: Consider moving majority of EventSignal to AI::Core::Details namespace, and refactoring Vision::Core::Events to inherit, and relay to private base + +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Clients can connect to the event signal to receive events, or disconnect from the event signal to stop receiving events. +/// +/// +/// At construction time, connect and disconnect callbacks can be provided that are called when +/// the number of connected clients changes from zero to one or one to zero, respectively. +/// +// +template +class EventSignalBase +{ +public: + /// + /// Constructs an event signal with empty connect and disconnect actions. + /// + EventSignalBase() : + m_nextCallbackToken(0) + { + } + + /// + /// Destructor. + /// + virtual ~EventSignalBase() + { + UnregisterAllCallbacks(); + } + + /// + /// Callback type that is used for signalling the event to connected clients. + /// + using CallbackFunction = std::function; + + /// + /// The argument type for the callback event + /// + using CallbackArgument = T; + + /// + /// A monotonically increasing token used for registration, tracking, and unregistration of callbacks. + /// + using CallbackToken = uint32_t; + + /// + /// Registers a callback to this EventSignalBase and assigns it a unique token. + /// + /// The callback to register. + /// + /// The new token associated with this registration that can be used for subsequent unregistration. + /// + CallbackToken RegisterCallback(CallbackFunction callback) + { + std::unique_lock lock(m_mutex); + + auto token = m_nextCallbackToken; + m_nextCallbackToken++; + + m_callbacks.emplace(token, callback); + + return token; + } + + /// + /// If present, unregisters a callback from this EventSource associated with the provided token. Tokens are + /// returned from RegisterCallback at the time of registration. + /// + /// + /// The token associated with the callback to be removed. This token is provided by the return value of + /// RegisterCallback at the time of registration. + /// + /// A value indicating whether any callback was unregistered in response to this request. + bool UnregisterCallback(CallbackToken token) + { + std::unique_lock lock(m_mutex); + return (bool)m_callbacks.erase(token); + } + + /// + /// Function call operator. + /// Signals the event with given arguments to connected clients, see also . + /// + /// Event arguments to signal. + void operator()(T t) + { + Signal(t); + } + + /// + /// Unregisters all registered callbacks. + /// + void UnregisterAllCallbacks() + { + std::unique_lock lock(m_mutex); + m_callbacks.clear(); + } + + /// + /// Signals the event with given arguments to all connected callbacks. + /// + /// Event arguments to signal. + void Signal(T t) + { + std::unique_lock lock(m_mutex); + + auto callbacksSnapshot = m_callbacks; + for (auto callbackCopyPair : callbacksSnapshot) + { + // now, while a callback is in progress, it can disconnect itself and any other connected + // callback. Check to see if the next one stored in the copy container is still connected. + bool stillConnected = (std::find_if(m_callbacks.begin(), m_callbacks.end(), + [&](const std::pair item) { + return callbackCopyPair.first == item.first; + }) != m_callbacks.end()); + + if (stillConnected) + { + callbackCopyPair.second(t); + } + } + } + + /// + /// Checks if a callback is connected. + /// + /// true if a callback is connected + bool IsConnected() const + { + std::unique_lock lock(m_mutex); + return !m_callbacks.empty(); + } + +protected: + std::map m_callbacks; + CallbackToken m_nextCallbackToken; + mutable std::recursive_mutex m_mutex; + +private: + EventSignalBase(const EventSignalBase&) = delete; + EventSignalBase(const EventSignalBase&&) = delete; + EventSignalBase& operator=(const EventSignalBase&) = delete; +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_file_logger.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_file_logger.h new file mode 100644 index 0000000..7638d09 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_file_logger.h @@ -0,0 +1,115 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Diagnostics { +namespace Logging { + +/// +/// Class with static methods to control file-based SDK logging. +/// Turning on logging while running your Speech SDK scenario provides +/// detailed information from the SDK's core native components. If you +/// report an issue to Microsoft, you may be asked to provide logs to help +/// Microsoft diagnose the issue. Your application should not take dependency +/// on particular log strings, as they may change from one SDK release to another +/// without notice. +/// FileLogger is the simplest logging solution and suitable for diagnosing +/// most on-device issues when running Speech SDK. +/// Added in version 1.20.0 +/// +/// File logging is a process wide construct. That means that if (for example) +/// you have multiple speech recognizer objects running in parallel, there will be one +/// log file containing interleaved logs lines from all recognizers. You cannot get a +/// separate log file for each recognizer. +class FileLogger +{ +public: + /// + /// Starts logging to a file. + /// + /// Path to a log file on local disk + /// Optional. If true, appends to existing log file. If false, creates a new log file + /// Note that each write operation to the file is immediately followed by a flush to disk. + /// For typical usage (e.g. one Speech Recognizer and a Solid State Drive (SSD)) this should not + /// cause performace issues. You may however want to avoid file logging when running many Speech + /// SDK recognizers or other SDK objects simultaneously. Use MemoryLogger or EventLogger instead. + static void Start(const SPXSTRING& filePath, bool append = false) + { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, filePath.empty()); + + SPX_THROW_ON_FAIL(property_bag_create(&hpropbag)); + SPX_THROW_ON_FAIL(property_bag_set_string(hpropbag, -1, "SPEECH-LogFilename", Utils::ToUTF8(filePath).c_str())); + SPX_THROW_ON_FAIL(property_bag_set_string(hpropbag, -1, "SPEECH-AppendToLogFile", append ? "1" : "0")); + SPX_THROW_ON_FAIL(diagnostics_log_start_logging(hpropbag, nullptr)); + SPX_THROW_ON_FAIL(property_bag_release(hpropbag)); + } + + /// + /// Stops logging to a file. + /// + /// This call is optional. If logging as been started, + /// the log file will be written when the process exists normally. + static void Stop() + { + SPX_THROW_ON_FAIL(diagnostics_log_stop_logging()); + } + + /// + /// Sets or clears the filters that apply to file logging. + /// Once filters are set, the callback will be invoked only if the log string + /// contains at least one of the strings specified by the filters. The match is case sensitive. + /// + /// Optional. Filters to use, or an empty list to remove previously set filters. + static void SetFilters(std::initializer_list filters = {}) + { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(property_bag_create(&hpropbag)); + + PropBagSetFilter(hpropbag, filters); + + SPX_THROW_ON_FAIL(diagnostics_log_apply_properties(hpropbag, nullptr)); + SPX_THROW_ON_FAIL(property_bag_release(hpropbag)); + } + + /// + /// Sets the level of the messages to be captured by the logger + /// + /// Maximum level of detail to be captured by the logger. + static void SetLevel(Level level) + { + const auto levelStr = Details::LevelToString(level); + diagnostics_set_log_level("memory", levelStr); + } + +private: + static void PropBagSetFilter(AZAC_HANDLE hpropbag, std::initializer_list filters) + { + std::string str = ""; + + if (filters.size() > 0) + { + std::ostringstream filtersCollapsed; + std::copy(filters.begin(), filters.end(), std::ostream_iterator(filtersCollapsed, ";")); + str = filtersCollapsed.str(); + } + + SPX_THROW_ON_FAIL(property_bag_set_string(hpropbag, -1, "SPEECH-LogFileFilters", str.c_str())); + } +}; + +}}}}} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_grammar.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_grammar.h new file mode 100644 index 0000000..056e0a1 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_grammar.h @@ -0,0 +1,70 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_grammar.h: Public API declarations for Grammar C++ class +// + +#pragma once +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + + +/// +/// Represents base class grammar for customizing speech recognition. +/// Added in version 1.5.0. +/// +class Grammar +{ +public: + + /// + /// Creates a grammar from a storage ID. + /// Added in version 1.7.0. + /// + /// The persisted storage ID of the language model. + /// The grammar. + /// + /// Creating a grammar from a storage ID is only usable in specific scenarios and is not generally possible. + /// + static std::shared_ptr FromStorageId(const SPXSTRING& storageId) + { + SPXGRAMMARHANDLE hgrammar = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(grammar_create_from_storage_id(&hgrammar, Utils::ToUTF8(storageId.c_str()))); + + return std::make_shared(hgrammar); + } + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Grammar handle. + explicit Grammar(SPXGRAMMARHANDLE hgrammar = SPXHANDLE_INVALID) : m_hgrammar(hgrammar) { } + + /// + /// Destructor, does nothing. + /// + virtual ~Grammar() { } + + /// + /// Internal. Explicit conversion operator. + /// + /// A handle. + explicit operator SPXGRAMMARHANDLE() { return m_hgrammar; } + +protected: + /*! \cond PROTECTED */ + DISABLE_COPY_AND_MOVE(Grammar); + + SmartHandle m_hgrammar; + /*! \endcond */ +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_grammar_list.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_grammar_list.h new file mode 100644 index 0000000..1118bca --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_grammar_list.h @@ -0,0 +1,90 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_grammar_list.h: Public API declarations for GrammarList C++ class +// + +#pragma once +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + + +/// +/// Represents a list of grammars for dynamic grammar scenarios. +/// Added in version 1.7.0. +/// +/// +/// GrammarLists are only usable in specific scenarios and are not generally available. +/// +class GrammarList : public Grammar +{ +public: + + /// + /// Creates a grammar lsit for the specified recognizer. + /// + /// The recognizer from which to obtain the grammar list. + /// The grammar list associated with the recognizer. + /// + /// Creating a grammar list from a recognizer is only usable in specific scenarios and is not generally available. + /// + template + static std::shared_ptr FromRecognizer(std::shared_ptr recognizer) + { + SPXRECOHANDLE hreco = recognizer != nullptr + ? (SPXRECOHANDLE)(*recognizer.get()) + : SPXHANDLE_INVALID; + + SPXGRAMMARHANDLE hgrammar = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(grammar_list_from_recognizer(&hgrammar, hreco)); + + return std::make_shared(hgrammar); + } + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// GrammarList handle. + explicit GrammarList(SPXGRAMMARHANDLE hgrammar = SPXHANDLE_INVALID) : Grammar(hgrammar) { } + + /// + /// Adds a single grammar to the current grammar list + /// + /// The grammar to add + /// + /// Currently Class Language Models are the only support grammars to add. + /// + template + void Add(std::shared_ptr grammar) + { + SPX_THROW_ON_FAIL(grammar_list_add_grammar(m_hgrammar.get(), (SPXGRAMMARHANDLE)(*grammar.get()))); + } + + /// + /// Sets the Recognition Factor applied to all grammars in a recognizer's GrammarList + /// + /// The RecognitionFactor to apply + /// The scope for the Recognition Factor being set + /// + /// The Recognition Factor is a numerical value greater than 0 modifies the default weight applied to supplied grammars. + /// Setting the Recognition Factor to 0 will disable the supplied grammars. + /// The default Recognition Factor is 1. + /// + void SetRecognitionFactor(double factor, RecognitionFactorScope scope) + { + SPX_THROW_ON_FAIL(grammar_list_set_recognition_factor(m_hgrammar.get(), factor, (GrammarList_RecognitionFactorScope)scope)); + } + +private: + DISABLE_COPY_AND_MOVE(GrammarList); +}; + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_grammar_phrase.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_grammar_phrase.h new file mode 100644 index 0000000..2c35b9d --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_grammar_phrase.h @@ -0,0 +1,64 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_phrase_grammar.h: Public API declarations for GrammarPhrase C++ class +// + +#pragma once +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + + +/// +/// Represents a phrase that may be spoken by the user. +/// Added in version 1.5.0. +/// +class GrammarPhrase +{ +public: + + /// + /// Creates a grammar phrase using the specified phrase text. + /// + /// The text representing a phrase that may be spoken by the user. + /// A shared pointer to a grammar phrase. + static std::shared_ptr From(const SPXSTRING& text) + { + SPXPHRASEHANDLE hphrase = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(grammar_phrase_create_from_text(&hphrase, Utils::ToUTF8(text).c_str())); + return std::make_shared(hphrase); + } + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Grammar phrase handle. + explicit GrammarPhrase(SPXPHRASEHANDLE hphrase) : m_hphrase(hphrase) { }; + + /// + /// Virtual destructor + /// + virtual ~GrammarPhrase() { } + + /// + /// Internal. Explicit conversion operator. + /// + /// A handle. + explicit operator SPXPHRASEHANDLE() { return m_hphrase; } + +private: + + DISABLE_DEFAULT_CTORS(GrammarPhrase); + + SmartHandle m_hphrase; +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_hybrid_speech_config.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_hybrid_speech_config.h new file mode 100644 index 0000000..39ca52e --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_hybrid_speech_config.h @@ -0,0 +1,161 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_hybrid_speech_config.h: Public API declarations for HybridSpeechConfig C++ class +// +#pragma once + +#include + +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Class that defines hybrid (cloud and embedded) configurations for speech recognition or speech synthesis. +/// +class HybridSpeechConfig +{ +protected: + /*! \cond PROTECTED */ + + SpeechConfig m_config; + + /*! \endcond */ + +public: + /// + /// Internal operator used to get the underlying handle value. + /// + /// A handle. + explicit operator SPXSPEECHCONFIGHANDLE() const + { + return static_cast(m_config); + } + + /// + /// Creates an instance of the hybrid speech config with specified cloud and embedded speech configs. + /// + /// A shared smart pointer of a cloud speech config. + /// A shared smart pointer of an embedded speech config. + /// A shared pointer to the new hybrid speech config instance. + static std::shared_ptr FromConfigs( + std::shared_ptr cloudSpeechConfig, + std::shared_ptr embeddedSpeechConfig) + { + SPXSPEECHCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + + SPX_THROW_ON_FAIL(hybrid_speech_config_create( + &hconfig, + Utils::HandleOrInvalid(cloudSpeechConfig), + Utils::HandleOrInvalid(embeddedSpeechConfig))); + + auto ptr = new HybridSpeechConfig(hconfig); + return std::shared_ptr(ptr); + } + + /// + /// Sets the speech recognition output format. + /// + /// Speech recognition output format (simple or detailed). + void SetSpeechRecognitionOutputFormat(OutputFormat format) + { + m_config.SetOutputFormat(format); + } + + /// + /// Gets the speech recognition output format. + /// + /// Speech recognition output format (simple or detailed). + OutputFormat GetSpeechRecognitionOutputFormat() const + { + return m_config.GetOutputFormat(); + } + + /// + /// Sets the speech synthesis output format (e.g. Riff16Khz16BitMonoPcm). + /// + /// Specifies the output format ID + void SetSpeechSynthesisOutputFormat(SpeechSynthesisOutputFormat formatId) + { + m_config.SetSpeechSynthesisOutputFormat(formatId); + } + + /// + /// Gets the speech synthesis output format. + /// + /// The speech synthesis output format. + SPXSTRING GetSpeechSynthesisOutputFormat() const + { + return m_config.GetSpeechSynthesisOutputFormat(); + } + + /// + /// Sets a property value by name. + /// + /// The property name. + /// The property value. + void SetProperty(const SPXSTRING& name, const SPXSTRING& value) + { + m_config.SetProperty(name, value); + } + + /// + /// Sets a property value by ID. + /// + /// The property id. + /// The property value. + void SetProperty(PropertyId id, const SPXSTRING& value) + { + m_config.SetProperty(id, value); + } + + /// + /// Gets a property value by name. + /// + /// The parameter name. + /// The property value. + SPXSTRING GetProperty(const SPXSTRING& name) const + { + return m_config.GetProperty(name); + } + + /// + /// Gets a property value by ID. + /// + /// The parameter id. + /// The property value. + SPXSTRING GetProperty(PropertyId id) const + { + return m_config.GetProperty(id); + } + + /// + /// Destructs the object. + /// + virtual ~HybridSpeechConfig() = default; + +protected: + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + inline explicit HybridSpeechConfig(SPXSPEECHCONFIGHANDLE hconfig) : m_config(hconfig) + { + } + + /*! \endcond */ + +private: + DISABLE_COPY_AND_MOVE(HybridSpeechConfig); + + }; + +}}} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_intent_recognition_eventargs.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_intent_recognition_eventargs.h new file mode 100644 index 0000000..dda06e8 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_intent_recognition_eventargs.h @@ -0,0 +1,169 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_intent_recognition_eventargs.h: Public API declarations for IntentRecognitionEventArgs C++ class +// + +#pragma once +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Intent { + + +/// +/// Class for intent recognition event arguments. +/// +class IntentRecognitionEventArgs : public RecognitionEventArgs +{ +private: + + SPXEVENTHANDLE m_hevent; + std::shared_ptr m_result; + +public: + + /// + /// Constructor. + /// + /// Event handle + explicit IntentRecognitionEventArgs(SPXEVENTHANDLE hevent) : + RecognitionEventArgs(hevent), + m_hevent(hevent), + m_result(std::make_shared(IntentResultHandleFromEventHandle(hevent))), + Result(m_result) + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hevent); + }; + + /// + virtual ~IntentRecognitionEventArgs() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hevent); + SPX_THROW_ON_FAIL(recognizer_event_handle_release(m_hevent)); + }; + +#if defined(BINDING_OBJECTIVE_C) +private: +#endif + /// + /// Intent recognition event result. + /// + std::shared_ptr Result; + +#if defined(BINDING_OBJECTIVE_C) +public: +#else +protected: +#endif + + /*! \cond PROTECTED */ + + /// + /// Intent recognition event result. + /// + std::shared_ptr GetResult() const { return m_result; } + + /*! \endcond */ + +private: + + DISABLE_DEFAULT_CTORS(IntentRecognitionEventArgs); + + SPXRESULTHANDLE IntentResultHandleFromEventHandle(SPXEVENTHANDLE hevent) + { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(recognizer_recognition_event_get_result(hevent, &hresult)); + return hresult; + } +}; + + +/// +/// Class for intent recognition canceled event arguments. +/// +class IntentRecognitionCanceledEventArgs final : public IntentRecognitionEventArgs +{ +private: + + std::shared_ptr m_cancellation; + CancellationReason m_cancellationReason; + CancellationErrorCode m_errorCode; + +public: + + /// + /// Constructor. + /// + /// Event handle + explicit IntentRecognitionCanceledEventArgs(SPXEVENTHANDLE hevent) : + IntentRecognitionEventArgs(hevent), + m_cancellation(CancellationDetails::FromResult(GetResult())), + m_cancellationReason(m_cancellation->Reason), + m_errorCode(m_cancellation->ErrorCode), + Reason(m_cancellationReason), + ErrorCode(m_errorCode), + ErrorDetails(m_cancellation->ErrorDetails) + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p)", __FUNCTION__, (void*)this); + }; + + /// + virtual ~IntentRecognitionCanceledEventArgs() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p)", __FUNCTION__, (void*)this); + }; + +#if defined(BINDING_OBJECTIVE_C) +private: +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif + /// + /// The reason the result was canceled. + /// + const CancellationReason& Reason; + + /// + /// The error code in case of an unsuccessful recognition ( is set to Error). + /// If Reason is not Error, ErrorCode is set to NoError. + /// Added in version 1.1.0. + /// + const CancellationErrorCode& ErrorCode; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + /// + /// The error message in case of an unsuccessful recognition ( is set to Error). + /// + const SPXSTRING ErrorDetails; + +#if defined(BINDING_OBJECTIVE_C) +public: +#else +private: +#endif + /// + /// CancellationDetails. + /// + std::shared_ptr GetCancellationDetails() const { return m_cancellation; } + +private: + + DISABLE_DEFAULT_CTORS(IntentRecognitionCanceledEventArgs); +}; + + +} } } } // Microsoft::CognitiveServices::Speech::Intent diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_intent_recognition_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_intent_recognition_result.h new file mode 100644 index 0000000..360e846 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_intent_recognition_result.h @@ -0,0 +1,119 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_intent_recognition_result.h: Public API declarations for IntentRecognitionResult C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#include "speechapi_c_json.h" + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Intent { + +/// +/// Represents the result of an intent recognition. +/// +class IntentRecognitionResult final : public RecognitionResult +{ +public: + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Result handle. + explicit IntentRecognitionResult(SPXRESULTHANDLE hresult) : + RecognitionResult(hresult), + IntentId(m_intentId) + { + PopulateIntentFields(hresult, &m_intentId); + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p) -- resultid=%s; reason=0x%x; text=%s", __FUNCTION__, (void*)this, (void*)Handle, Utils::ToUTF8(ResultId).c_str(), Reason, Utils::ToUTF8(Text).c_str()); + } + + /// + /// A call to return a map of the entities found in the utterance. + /// + /// + /// A map with the entity name as a key and containing the value of the entity found in the utterance. + /// + /// + /// This currently does not report LUIS entities. + /// + const std::map& GetEntities() const + { + return m_entities; + } + + /// + /// Destructor. + /// + ~IntentRecognitionResult() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)Handle); + } + + /// + /// Unique intent id. + /// + const SPXSTRING& IntentId; + +private: + DISABLE_DEFAULT_CTORS(IntentRecognitionResult); + + void PopulateIntentFields(SPXRESULTHANDLE hresult, SPXSTRING* pintentId) + { + SPX_INIT_HR(hr); + + const size_t maxCharCount = 1024; + char sz[maxCharCount+1] = {}; + + if (pintentId != nullptr && recognizer_result_handle_is_valid(hresult)) + { + SPX_THROW_ON_FAIL(hr = intent_result_get_intent_id(hresult, sz, maxCharCount)); + *pintentId = Utils::ToSPXString(sz); + } + + auto jsonSLE = Properties.GetProperty("LanguageUnderstandingSLE_JsonResult"); + SPXHANDLE parserHandle = SPXHANDLE_INVALID; + auto scopeGuard = Utils::MakeScopeGuard([&parserHandle]() + { + if (parserHandle != SPXHANDLE_INVALID) + { + ai_core_json_parser_handle_release(parserHandle); + } + }); + + auto root = ai_core_json_parser_create(&parserHandle, jsonSLE.c_str(), jsonSLE.size()); + int count = ai_core_json_item_count(parserHandle, root); + for (int i = 0; i < count; i++) + { + auto itemInt = ai_core_json_item_at(parserHandle, root, i, nullptr); + auto nameInt = ai_core_json_item_name(parserHandle, itemInt); + + // Need to use string copy here to force the ajv json parser to convert back to utf8. + auto name = ai_core_json_value_as_string_copy(parserHandle, nameInt, ""); + auto value = ai_core_json_value_as_string_copy(parserHandle, itemInt, ""); + if (value != nullptr && name != nullptr) + { + m_entities[name] = value; + } + } + + } + + SPXSTRING m_intentId; + std::map m_entities; +}; + + +} } } } // Microsoft::CognitiveServices::Speech::Recognition::Intent diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_intent_recognizer.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_intent_recognizer.h new file mode 100644 index 0000000..473053d --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_intent_recognizer.h @@ -0,0 +1,513 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_intent_recognizer.h: Public API declarations for IntentRecognizer C++ class +// + +#pragma once +#include +#include +#include +#include "speechapi_c_json.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Intent { + +/// +/// In addition to performing speech-to-text recognition, the IntentRecognizer extracts structured information +/// about the intent of the speaker, which can be used to drive further actions using dedicated intent triggers +/// (see ). +/// + class IntentRecognizer : public AsyncRecognizer + { + public: + + using BaseType = AsyncRecognizer; + + /// + /// Creates an intent recognizer from a speech config and an audio config. + /// Users should use this function to create a new instance of an intent recognizer. + /// + /// Speech configuration. + /// Audio configuration. + /// Instance of intent recognizer. + static std::shared_ptr FromConfig(std::shared_ptr speechConfig, std::shared_ptr audioInput = nullptr) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_intent_recognizer_from_config( + &hreco, + HandleOrInvalid(speechConfig), + HandleOrInvalid(audioInput))); + return std::make_shared(hreco); + } + + /// + /// Creates an intent recognizer from an embedded speech config and an audio config. + /// Users should use this function to create a new instance of an intent recognizer. + /// Added in version 1.19.0 + /// + /// Embedded speech configuration. + /// Audio configuration. + /// Instance of intent recognizer. + static std::shared_ptr FromConfig(std::shared_ptr speechConfig, std::shared_ptr audioInput = nullptr) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_intent_recognizer_from_config( + &hreco, + HandleOrInvalid(speechConfig), + HandleOrInvalid(audioInput))); + return std::make_shared(hreco); + } + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Recognizer handle. + explicit IntentRecognizer(SPXRECOHANDLE hreco) : BaseType(hreco), Properties(m_properties) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + } + + /// + /// destructor + /// + ~IntentRecognizer() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + TermRecognizer(); + } + + /// + /// Starts intent recognition, and returns after a single utterance is recognized. The end of a + /// single utterance is determined by listening for silence at the end or until a maximum of about 30 + /// seconds of audio is processed. The task returns the recognition text as result. + /// Note: Since RecognizeOnceAsync() returns only a single utterance, it is suitable only for single + /// shot recognition like command or query. + /// For long-running multi-utterance recognition, use StartContinuousRecognitionAsync() instead.. + /// + /// Future containing result value (a shared pointer to IntentRecognitionResult) + /// of the asynchronous intent recognition. + /// + std::future> RecognizeOnceAsync() override + { + return BaseType::RecognizeOnceAsyncInternal(); + } + + /// + /// Starts intent recognition, and generates a result from the text passed in. This is useful for testing and other times when the speech input + /// is not tied to the IntentRecognizer. + /// Note: The Intent Service does not currently support this so it is only valid for offline pattern matching or exact matching intents. + /// + /// The text to be evaluated. + /// Future containing result value (a shared pointer to IntentRecognitionResult) + /// of the asynchronous intent recognition. + /// + std::future> RecognizeOnceAsync(SPXSTRING text) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this, text]() -> std::shared_ptr { + SPX_INIT_HR(hr); + + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(hr = intent_recognizer_recognize_text_once(m_hreco, Utils::ToUTF8(text).c_str(), &hresult)); + + return std::make_shared(hresult); + }); + return future; + } + + /// + /// Asynchronously initiates continuous intent recognition operation. + /// + /// An empty future. + std::future StartContinuousRecognitionAsync() override + { + return BaseType::StartContinuousRecognitionAsyncInternal(); + } + + /// + /// Asynchronously terminates ongoing continuous intent recognition operation. + /// + /// An empty future. + std::future StopContinuousRecognitionAsync() override + { + return BaseType::StopContinuousRecognitionAsyncInternal(); + } + + /// + /// Asynchronously initiates keyword recognition operation. + /// + /// Specifies the keyword model to be used. + /// An empty future. + std::future StartKeywordRecognitionAsync(std::shared_ptr model) override + { + return BaseType::StartKeywordRecognitionAsyncInternal(model); + } + + /// + /// Asynchronously terminates keyword recognition operation. + /// + /// An empty future. + std::future StopKeywordRecognitionAsync() override + { + return BaseType::StopKeywordRecognitionAsyncInternal(); + } + + /// + /// A collection of properties and their values defined for this . + /// + PropertyCollection& Properties; + + /// + /// Adds a simple phrase that may be spoken by the user, indicating a specific user intent. + /// This simple phrase can be a pattern including and enitity surrounded by braces. Such as "click the {checkboxName} checkbox". + /// + /// + /// The phrase corresponding to the intent. + /// Once recognized, the IntentRecognitionResult's IntentId property will match the simplePhrase specified here. + /// If any entities are specified and matched, they will be available in the IntentResult->GetEntities() call. + /// + void AddIntent(const SPXSTRING& simplePhrase) + { + auto trigger = IntentTrigger::From(simplePhrase); + return AddIntent(trigger, simplePhrase); + } + + /// + /// Adds a simple phrase that may be spoken by the user, indicating a specific user intent. + /// This simple phrase can be a pattern including and enitity surrounded by braces. Such as "click the {checkboxName} checkbox". + /// + /// The phrase corresponding to the intent. + /// A custom id string to be returned in the IntentRecognitionResult's IntentId property. + /// Once recognized, the result's intent id will match the id supplied here. + /// If any entities are specified and matched, they will be available in the IntentResult->GetEntities() call. + /// + void AddIntent(const SPXSTRING& simplePhrase, const SPXSTRING& intentId) + { + auto trigger = IntentTrigger::From(simplePhrase); + return AddIntent(trigger, intentId); + } + + /// + /// Adds a single intent by name from the specified Language Understanding Model. + /// For PatternMatchingModel and ConversationalLanguageUnderstandingModel types, this will clear + /// any existing models before enabling it. For these types, the intentName is ignored. + /// + /// The language understanding model containing the intent. + /// The name of the single intent to be included from the language understanding model. + /// Once recognized, the IntentRecognitionResult's IntentId property will contain the intentName specified here. + void AddIntent(std::shared_ptr model, const SPXSTRING& intentName) + { + switch (model->GetModelType()) + { + case LanguageUnderstandingModel::LanguageUnderstandingModelType::LanguageUnderstandingModel: + { + auto trigger = IntentTrigger::From(model, intentName); + AddIntent(trigger, intentName); + break; + } + case LanguageUnderstandingModel::LanguageUnderstandingModelType::PatternMatchingModel: + { + intent_recognizer_clear_language_models(m_hreco); + AddPatternMatchingModel(model); + break; + } + case LanguageUnderstandingModel::LanguageUnderstandingModelType::ConversationalLanguageUnderstandingModel: + { + intent_recognizer_clear_language_models(m_hreco); + auto cluModel = static_cast(model.get()); + intent_recognizer_add_conversational_language_understanding_model( + m_hreco, + cluModel->languageResourceKey.c_str(), + cluModel->endpoint.c_str(), + cluModel->projectName.c_str(), + cluModel->deploymentName.c_str()); + break; + } + default: + break; + } + } + + /// + /// Adds a single intent by name from the specified Language Understanding Model. + /// For PatternMatchingModel and ConversationalLanguageUnderstandingModel types, this will clear + /// any existing models before enabling it. For these types, the intentName and intentId are ignored. + /// + /// The language understanding model containing the intent. + /// The name of the single intent to be included from the language understanding model. + /// A custom id string to be returned in the IntentRecognitionResult's IntentId property. + void AddIntent(std::shared_ptr model, const SPXSTRING& intentName, const SPXSTRING& intentId) + { + switch (model->GetModelType()) + { + case LanguageUnderstandingModel::LanguageUnderstandingModelType::LanguageUnderstandingModel: + { + auto trigger = IntentTrigger::From(model, intentName); + AddIntent(trigger, intentId); + break; + } + case LanguageUnderstandingModel::LanguageUnderstandingModelType::PatternMatchingModel: + { + intent_recognizer_clear_language_models(m_hreco); + AddPatternMatchingModel(model); + break; + } + case LanguageUnderstandingModel::LanguageUnderstandingModelType::ConversationalLanguageUnderstandingModel: + { + intent_recognizer_clear_language_models(m_hreco); + auto cluModel = static_cast(model.get()); + intent_recognizer_add_conversational_language_understanding_model( + m_hreco, + cluModel->languageResourceKey.c_str(), + cluModel->endpoint.c_str(), + cluModel->projectName.c_str(), + cluModel->deploymentName.c_str()); + break; + } + default: + break; + } + } + + /// + /// Adds all intents from the specified Language Understanding Model. + /// For PatternMatchingModel and ConversationalLanguageUnderstandingModel types, this will clear + /// any existing models before enabling it. + /// + /// The language understanding model containing the intents. + /// Once recognized, the IntentRecognitionResult's IntentId property will contain the name of the intent recognized. + void AddAllIntents(std::shared_ptr model) + { + switch (model->GetModelType()) + { + case LanguageUnderstandingModel::LanguageUnderstandingModelType::LanguageUnderstandingModel: + { + auto trigger = IntentTrigger::From(model); + AddIntent(trigger, SPXSTRING_EMPTY); + break; + } + case LanguageUnderstandingModel::LanguageUnderstandingModelType::PatternMatchingModel: + { + intent_recognizer_clear_language_models(m_hreco); + AddPatternMatchingModel(model); + break; + } + case LanguageUnderstandingModel::LanguageUnderstandingModelType::ConversationalLanguageUnderstandingModel: + { + intent_recognizer_clear_language_models(m_hreco); + auto cluModel = static_cast(model.get()); + intent_recognizer_add_conversational_language_understanding_model( + m_hreco, + cluModel->languageResourceKey.c_str(), + cluModel->endpoint.c_str(), + cluModel->projectName.c_str(), + cluModel->deploymentName.c_str()); + break; + } + default: + break; + } + } + + /// + /// Adds all intents from the specified Language Understanding Model. + /// For PatternMatchingModel and ConversationalLanguageUnderstandingModel types, this will clear + /// any existing models before enabling it. + /// + /// The language understanding model containing the intents. + /// A custom string id to be returned in the IntentRecognitionResult's IntentId property. + void AddAllIntents(std::shared_ptr model, const SPXSTRING& intentId) + { + switch (model->GetModelType()) + { + case LanguageUnderstandingModel::LanguageUnderstandingModelType::LanguageUnderstandingModel: + { + auto trigger = IntentTrigger::From(model); + AddIntent(trigger, intentId); + break; + } + case LanguageUnderstandingModel::LanguageUnderstandingModelType::PatternMatchingModel: + { + intent_recognizer_clear_language_models(m_hreco); + AddPatternMatchingModel(model); + break; + } + case LanguageUnderstandingModel::LanguageUnderstandingModelType::ConversationalLanguageUnderstandingModel: + { + intent_recognizer_clear_language_models(m_hreco); + auto cluModel = static_cast(model.get()); + intent_recognizer_add_conversational_language_understanding_model( + m_hreco, + cluModel->languageResourceKey.c_str(), + cluModel->endpoint.c_str(), + cluModel->projectName.c_str(), + cluModel->deploymentName.c_str()); + break; + } + default: + break; + } + } + + /// + /// Adds the IntentTrigger specified. + /// + /// The IntentTrigger corresponding to the intent. + /// A custom string id to be returned in the IntentRecognitionResult's IntentId property. + void AddIntent(std::shared_ptr trigger, const SPXSTRING& intentId) + { + SPX_THROW_ON_FAIL(intent_recognizer_add_intent(m_hreco, Utils::ToUTF8(intentId).c_str(), (SPXTRIGGERHANDLE)(*trigger.get()))); + } + + /// + /// Sets the authorization token that will be used for connecting to the service. + /// Note: The caller needs to ensure that the authorization token is valid. Before the authorization token + /// expires, the caller needs to refresh it by calling this setter with a new valid token. + /// Otherwise, the recognizer will encounter errors during recognition. + /// + /// A string that represents the authorization token. + void SetAuthorizationToken(const SPXSTRING& token) + { + Properties.SetProperty(PropertyId::SpeechServiceAuthorization_Token, token); + } + + /// + /// Gets the authorization token. + /// + /// Authorization token + SPXSTRING GetAuthorizationToken() + { + return Properties.GetProperty(PropertyId::SpeechServiceAuthorization_Token, SPXSTRING()); + } + + /// + /// Takes a collection of language understanding models, makes a copy of them, and applies them to the recognizer. This application + /// happens at different times depending on the language understanding model type. + /// Simple Language Models will become active almost immediately whereas + /// language understanding models utilizing LUIS will become active on the next Speech turn. + /// This replaces any previously applied models. + /// + /// A vector of shared pointers to LanguageUnderstandingModels. + /// True if the application of the models takes effect immediately. Otherwise false. + bool ApplyLanguageModels(const std::vector>& collection) + { + bool result = true; + SPXTRIGGERHANDLE htrigger = SPXHANDLE_INVALID; + + // Clear existing language models. + SPX_THROW_ON_FAIL(intent_recognizer_clear_language_models(m_hreco)); + + // Add the new ones. + for (auto model : collection) + { + switch (model->GetModelType()) + { + case LanguageUnderstandingModel::LanguageUnderstandingModelType::LanguageUnderstandingModel: + SPX_THROW_ON_FAIL(intent_trigger_create_from_language_understanding_model(&htrigger, static_cast(*model), nullptr)); + intent_recognizer_add_intent(m_hreco, nullptr, htrigger); + result = false; + break; + case LanguageUnderstandingModel::LanguageUnderstandingModelType::PatternMatchingModel: + { + AddPatternMatchingModel(model); + break; + } + case LanguageUnderstandingModel::LanguageUnderstandingModelType::ConversationalLanguageUnderstandingModel: + { + auto cluModel = static_cast(model.get()); + intent_recognizer_add_conversational_language_understanding_model( + m_hreco, + cluModel->languageResourceKey.c_str(), + cluModel->endpoint.c_str(), + cluModel->projectName.c_str(), + cluModel->deploymentName.c_str()); + break; + } + default: + break; + } + + } + return result; + } + +private: + void AddPatternMatchingModel(const std::shared_ptr& luModel) const + { + auto model = static_cast(luModel.get()); + std::string modelId = model->GetModelId(); + + Utils::AbiHandle hModel(language_understanding_model__handle_release); + SPX_THROW_ON_FAIL(pattern_matching_model_create(&hModel, m_hreco, modelId.c_str())); + + PATTERN_MATCHING_MODEL_GET_STR_FROM_INDEX vectorGetter = [](void* context, size_t index, const char** phrase, size_t* phraseLen) -> AZACHR + { + try + { + SPX_RETURN_HR_IF(SPXERR_INVALID_ARG, context == nullptr || phrase == nullptr || phraseLen == nullptr); + + auto phrases = static_cast*>(context); + SPX_RETURN_HR_IF(SPXERR_OUT_OF_RANGE, index >= phrases->size()); + + *phrase = phrases->at(index).c_str(); + *phraseLen = phrases->at(index).length(); + return SPX_NOERROR; + } + catch (...) + { + return SPXERR_UNHANDLED_EXCEPTION; + } + }; + + for (const auto& entity : model->Entities) + { + SPX_THROW_ON_FAIL(pattern_matching_model_add_entity( + hModel, + entity.Id.c_str(), + (int)entity.Type, + (int)entity.Mode, + entity.Phrases.size(), + (void*)&entity.Phrases, + vectorGetter)); + } + + for (const auto& intent : model->Intents) + { + SPX_THROW_ON_FAIL(pattern_matching_model_add_intent( + hModel, + intent.Id.c_str(), + 0, // no priority at the moment so set to 0 + intent.Phrases.size(), + (void*)&intent.Phrases, + vectorGetter)); + } + + Utils::AbiHandle hTrigger(intent_trigger_handle_release); + SPX_THROW_ON_FAIL(intent_trigger_create_from_language_understanding_model(&hTrigger, hModel, "")); + + SPX_THROW_ON_FAIL(intent_recognizer_add_intent_with_model_id(m_hreco, hTrigger, modelId.c_str())); + } + + DISABLE_COPY_AND_MOVE(IntentRecognizer); + + friend class Microsoft::CognitiveServices::Speech::Session; +}; + + +} } } } // Microsoft::CognitiveServices::Speech::Intent diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_intent_trigger.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_intent_trigger.h new file mode 100644 index 0000000..b67babd --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_intent_trigger.h @@ -0,0 +1,87 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_intent_trigger.h: Public API declarations for IntentTrigger C++ class +// + +#pragma once +#include +#include +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Intent { + +/// +/// Represents an intent trigger. +/// +class IntentTrigger +{ +public: + + /// + /// Creates an intent trigger using the specified phrase. + /// + /// The simple phrase to create an intent trigger for. + /// A shared pointer to an intent trigger. + static std::shared_ptr From(const SPXSTRING& simplePhrase) + { + SPXTRIGGERHANDLE htrigger = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(intent_trigger_create_from_phrase(&htrigger, Utils::ToUTF8(simplePhrase).c_str())); + return std::make_shared(htrigger); + } + + /// + /// Creates an intent trigger using the specified LanguageUnderstandingModel. + /// + /// The LanguageUnderstandingModel to create an intent trigger for. + /// A shared pointer to an intent trigger. + static std::shared_ptr From(std::shared_ptr model) + { + SPXTRIGGERHANDLE htrigger = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(intent_trigger_create_from_language_understanding_model(&htrigger, (SPXLUMODELHANDLE)(*model.get()), nullptr)); + return std::make_shared(htrigger); + } + + /// + /// Creates an intent trigger using the specified LanguageUnderstandingModel and an intent name. + /// + /// The LanguageUnderstandingModel to create an intent trigger for. + /// The intent name to create an intent trigger for. + /// A shared pointer to an intent trigger. + static std::shared_ptr From(std::shared_ptr model, const SPXSTRING& intentName) + { + SPXTRIGGERHANDLE htrigger = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(intent_trigger_create_from_language_understanding_model(&htrigger, (SPXLUMODELHANDLE)(*model.get()), Utils::ToUTF8(intentName).c_str())); + return std::make_shared(htrigger); + } + + /// + /// Virtual destructor + /// + virtual ~IntentTrigger() { intent_trigger_handle_release(m_htrigger); m_htrigger = SPXHANDLE_INVALID; } + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Trigger handle. + explicit IntentTrigger(SPXTRIGGERHANDLE htrigger) : m_htrigger(htrigger) { }; + + /// + /// Internal. Explicit conversion operator. + /// + /// A handle. + explicit operator SPXTRIGGERHANDLE() { return m_htrigger; } + +private: + DISABLE_DEFAULT_CTORS(IntentTrigger); + + SPXTRIGGERHANDLE m_htrigger; +}; + + +} } } } // Microsoft::CognitiveServices::Speech::Intent diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_keyword_recognition_eventargs.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_keyword_recognition_eventargs.h new file mode 100644 index 0000000..24411d5 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_keyword_recognition_eventargs.h @@ -0,0 +1,86 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_keyword_recognition_eventargs.h: Public API declarations for KeywordRecognitionEventArgs C++ class +// + +#pragma once +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Class for the events emmited by the . +/// +class KeywordRecognitionEventArgs : public RecognitionEventArgs +{ +private: + + SPXEVENTHANDLE m_hevent; + std::shared_ptr m_result; + +public: + + /// + /// Constructor. + /// + /// Event handle + explicit KeywordRecognitionEventArgs(SPXEVENTHANDLE hevent) : + RecognitionEventArgs(hevent), + m_hevent(hevent), + m_result(std::make_shared(ResultHandleFromEventHandle(hevent))), + Result(m_result) + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hevent); + }; + + /// + virtual ~KeywordRecognitionEventArgs() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hevent); + SPX_THROW_ON_FAIL(recognizer_event_handle_release(m_hevent)); + }; + +#if defined(BINDING_OBJECTIVE_C) +private: +#endif + /// + /// Keyword recognition event result. + /// + std::shared_ptr Result; + +#if defined(BINDING_OBJECTIVE_C) +public: +#else +protected: +#endif + + /*! \cond PROTECTED */ + + /// + /// Speech recognition event result. + /// + std::shared_ptr GetResult() const { return m_result; } + + /*! \endcond */ + +private: + + DISABLE_DEFAULT_CTORS(KeywordRecognitionEventArgs); + + SPXRESULTHANDLE ResultHandleFromEventHandle(SPXEVENTHANDLE hevent) + { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(recognizer_recognition_event_get_result(hevent, &hresult)); + return hresult; + } +}; + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_keyword_recognition_model.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_keyword_recognition_model.h new file mode 100644 index 0000000..afa56cd --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_keyword_recognition_model.h @@ -0,0 +1,101 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_keyword_recognition_model.h: Public API declarations for KeywordRecognitionModel C++ class +// + +#pragma once +#include +#include +#include +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Represents keyword recognition model used with StartKeywordRecognitionAsync methods. +/// +class KeywordRecognitionModel +{ +public: + + /// + /// Creates a keyword recognition model using the specified file. + /// + /// The file name of the keyword recognition model. + /// A shared pointer to keyword recognition model. + static std::shared_ptr FromFile(const SPXSTRING& fileName) + { + SPXKEYWORDHANDLE hkeyword = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(keyword_recognition_model_create_from_file(Utils::ToUTF8(fileName).c_str(), &hkeyword)); + return std::make_shared(hkeyword); + } + + /// + /// Creates a keyword recognition model using the specified embedded speech config. + /// + /// Embedded speech config. + /// A shared pointer to keyword recognition model. + static std::shared_ptr FromConfig(std::shared_ptr embeddedSpeechConfig) + { + SPXKEYWORDHANDLE hkeyword = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(keyword_recognition_model_create_from_config( + Utils::HandleOrInvalid(embeddedSpeechConfig), &hkeyword)); + + return std::make_shared(hkeyword); + } + + /// + /// Creates a keyword recognition model using the specified embedded speech config + /// and user-defined wake words. + /// + /// Embedded speech config. + /// User-defined wake words. + /// A shared pointer to keyword recognition model. + static std::shared_ptr FromConfig( + std::shared_ptr embeddedSpeechConfig, const std::vector& userDefinedWakeWords) + { + SPXKEYWORDHANDLE hkeyword = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(keyword_recognition_model_create_from_config( + Utils::HandleOrInvalid(embeddedSpeechConfig), &hkeyword)); + + for (const SPXSTRING& wakeWord : userDefinedWakeWords) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, wakeWord.empty()); + SPX_THROW_ON_FAIL(keyword_recognition_model_add_user_defined_wake_word( + static_cast(hkeyword), Utils::ToUTF8(wakeWord).c_str())); + } + + return std::make_shared(hkeyword); + } + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Keyword handle. + explicit KeywordRecognitionModel(SPXKEYWORDHANDLE hkeyword = SPXHANDLE_INVALID) : m_hkwmodel(hkeyword) { } + + /// + /// Virtual destructor. + /// + virtual ~KeywordRecognitionModel() { keyword_recognition_model_handle_release(m_hkwmodel); } + + /// + /// Internal. Explicit conversion operator. + /// + /// A handle. + explicit operator SPXKEYWORDHANDLE() { return m_hkwmodel; } + +private: + + DISABLE_COPY_AND_MOVE(KeywordRecognitionModel); + + SPXKEYWORDHANDLE m_hkwmodel; +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_keyword_recognition_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_keyword_recognition_result.h new file mode 100644 index 0000000..ddaa7ee --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_keyword_recognition_result.h @@ -0,0 +1,44 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_keyword_recognition_result.h: Public API declarations for the KeywordRecognitionResult C++ class +// + +#pragma once + +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Class that defines the results emitted by the . +/// +class KeywordRecognitionResult : public RecognitionResult +{ +public: + + explicit KeywordRecognitionResult(SPXRESULTHANDLE hresult) : + RecognitionResult(hresult) + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p) -- resultid=%s; reason=0x%x; text=%s", __FUNCTION__, (void*)this, (void*)Handle, Utils::ToUTF8(ResultId).c_str(), Reason, Utils::ToUTF8(Text).c_str()); + } + + virtual ~KeywordRecognitionResult() = default; + +private: + DISABLE_DEFAULT_CTORS(KeywordRecognitionResult); +}; + +inline std::shared_ptr AudioDataStream::FromResult(std::shared_ptr result) +{ + auto resultHandle = result != nullptr ? static_cast(*result) : SPXHANDLE_INVALID; + auto streamHandle = Utils::CallFactoryMethodLeft(audio_data_stream_create_from_keyword_result, resultHandle); + return std::shared_ptr{ new AudioDataStream(streamHandle) }; +} + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_keyword_recognizer.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_keyword_recognizer.h new file mode 100644 index 0000000..3807868 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_keyword_recognizer.h @@ -0,0 +1,213 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_keyword_recognizer.h: Public API declarations for KeywordRecognizer C++ class +// +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +namespace Utils { + template + std::function&)> Callback(U* callee, F f) + { + return [=](const EventSignal& evt) + { + (callee->*f)(evt); + }; + } +} + +/// +/// Recognizer type that is specialized to only handle keyword activation. +/// +/// +/// First, the object needs to be instantiated: +/// +/// auto audioConfig = AudioConfig::FromMicrophoneInput(); // Or an alternative input +/// auto recognizer = KeywordRecognizer::FromConfig(audioConfig); +/// +/// (optional) Then, the events need to be wired in order to receive notifications: +/// +/// recognizer->Recognized += [](const KeywordRecognitionEventArgs& event) +/// { +/// // Your logic here... +/// }; +/// +/// And finally, recognition needs to be started. +/// +/// auto keywordModel = KeywordRecognitionModel::FromFile(modelPath); +/// auto resultFuture = recognizer->RecognizeKeywordOnceAsync(keywordModel); +/// resultFuture.wait(); +/// auto result = resultFuture.get(); +/// +///
    +///
  • +///
  • +///
  • +///
  • +///
+///
+class KeywordRecognizer: public std::enable_shared_from_this +{ +public: + /// + /// Creates a KeywordRecognizer from an . The config is intended + /// to define the audio input to be used by the recognizer object. + /// + /// Defines the audio input to be used by the recognizer. + /// A new KeywordRecognizer that will consume audio from the specified input. + /// + /// If no audio config is provided, it will be equivalent to calling with a config constructed with + /// + /// + inline static std::shared_ptr FromConfig(std::shared_ptr audioConfig = nullptr) + { + auto hreco = Utils::CallFactoryMethodLeft( + ::recognizer_create_keyword_recognizer_from_audio_config, + Utils::HandleOrInvalid(audioConfig)); + return std::shared_ptr(new KeywordRecognizer(hreco)); + } + + /// + /// Destructor. + /// + ~KeywordRecognizer() + { + Canceled.DisconnectAll(); + Recognized.DisconnectAll(); + recognizer_handle_release(m_handle); + } + + /// + /// Starts a keyword recognition session. This session will last until the first keyword is recognized. When this happens, + /// a event will be raised and the session will end. To rearm the keyword, the method needs to be called + /// again after the event is emitted. + /// + /// The that describes the keyword we want to detect. + /// A future that resolves to a that resolves once a keyword is detected. + /// + /// Note that if no keyword is detected in the input, the task will never resolve (unless is called. + /// + inline std::future> RecognizeOnceAsync(std::shared_ptr model) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, model, this]() + { + auto modelHandle = static_cast(*model); + + SPXRESULTHANDLE result = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(recognizer_recognize_keyword_once(m_handle, modelHandle, &result)); + + return std::make_shared(result); + }); + return future; + } + + /// + /// Stops a currently active keyword recognition session. + /// + /// A future that resolves when the active session (if any) is stopped. + inline std::future StopRecognitionAsync() + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this]() + { + SPX_THROW_ON_FAIL(recognizer_stop_keyword_recognition(m_handle)); + }); + return future; + } + + /// + /// Signal for events related to the recognition of keywords. + /// + EventSignal Recognized; + + /// + /// Signal for events relating to the cancellation of an interaction. The event indicates if the reason is a direct cancellation or an error. + /// + EventSignal Canceled; + +private: + /*! \cond PROTECTED */ + + static void FireEvent_Recognized(SPXRECOHANDLE, SPXEVENTHANDLE h_event, void* pv_context) + { + auto keep_alive = static_cast(pv_context)->shared_from_this(); + KeywordRecognitionEventArgs event{ h_event }; + keep_alive->Recognized.Signal(event); + } + + static void FireEvent_Canceled(SPXRECOHANDLE, SPXEVENTHANDLE h_event, void* pv_context) + { + auto keep_alive = static_cast(pv_context)->shared_from_this(); + SpeechRecognitionCanceledEventArgs event{ h_event }; + keep_alive->Canceled.Signal(event); + } + + void RecognizerEventConnectionChanged(const EventSignal& reco_event) + { + if (m_handle != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_handle=0x%8p", __FUNCTION__, (void*)m_handle); + SPX_DBG_TRACE_VERBOSE_IF(!::recognizer_handle_is_valid(m_handle), "%s: m_handle is INVALID!!!", __FUNCTION__); + + if (&reco_event== &Recognized) + { + ::recognizer_recognized_set_callback(m_handle, Recognized.IsConnected() ? KeywordRecognizer::FireEvent_Recognized : nullptr, this); + } + } + } + + void CanceledEventConnectionChanged(const EventSignal& canceled_event) + { + if (m_handle != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_handle=0x%8p", __FUNCTION__, (void*)m_handle); + SPX_DBG_TRACE_VERBOSE_IF(!::recognizer_handle_is_valid(m_handle), "%s: m_handle is INVALID!!!", __FUNCTION__); + + if (&canceled_event == &Canceled) + { + ::recognizer_canceled_set_callback(m_handle, Canceled.IsConnected() ? KeywordRecognizer::FireEvent_Canceled : nullptr, this); + } + } + } + + inline explicit KeywordRecognizer(SPXRECOHANDLE handle): + Recognized{ Utils::Callback(this, &KeywordRecognizer::RecognizerEventConnectionChanged) }, + Canceled{ Utils::Callback(this, &KeywordRecognizer::CanceledEventConnectionChanged) }, + m_properties{ Utils::CallFactoryMethodRight(recognizer_get_property_bag, handle) }, + m_handle{ handle }, + Properties { m_properties } + { + } + + PropertyCollection m_properties; + SPXRECOHANDLE m_handle; + /*! \endcond */ + +public: + /// + /// A collection of properties and their values defined for this . + /// + const PropertyCollection& Properties; +}; + + +} } } diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_language_understanding_model.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_language_understanding_model.h new file mode 100644 index 0000000..343a6b6 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_language_understanding_model.h @@ -0,0 +1,113 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_language_understanding_model.h: Public API declarations for LanguageUnderstandingModel C++ class +// + +#pragma once +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Intent { + +/// +/// Represents language understanding model used for intent recognition. +/// +class LanguageUnderstandingModel +{ +public: + + enum class LanguageUnderstandingModelType + { + PatternMatchingModel, + LanguageUnderstandingModel, + ConversationalLanguageUnderstandingModel + }; + + /// + /// Creates a language understanding (LUIS) model using the specified endpoint url. + /// + /// The endpoint url of a language understanding model. + /// A shared pointer to language understanding model. + static std::shared_ptr FromEndpoint(const SPXSTRING& uri) + { + SPXLUMODELHANDLE hlumodel = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(language_understanding_model_create_from_uri(&hlumodel, Utils::ToUTF8(uri).c_str())); + return std::make_shared(hlumodel); + } + + /// + /// Creates a language understanding model using the specified app id. + /// + /// A string that represents the application id of Language Understanding service. + /// A shared pointer to language understanding model. + static std::shared_ptr FromAppId(const SPXSTRING& appId) + { + SPXLUMODELHANDLE hlumodel = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(language_understanding_model_create_from_app_id(&hlumodel, Utils::ToUTF8(appId).c_str())); + return std::make_shared(hlumodel); + } + + /// + /// Creates a language understanding model using the specified hostname, subscription key and application id. + /// + /// A string that represents the subscription key of Language Understanding service. + /// A string that represents the application id of Language Understanding service. + /// A String that represents the region of the Language Understanding service (see the region page). + /// A shared pointer to language understanding model. + static std::shared_ptr FromSubscription(const SPXSTRING& subscriptionKey, const SPXSTRING& appId, const SPXSTRING& region) + { + SPXLUMODELHANDLE hlumodel = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(language_understanding_model_create_from_subscription(&hlumodel, Utils::ToUTF8(subscriptionKey).c_str(), Utils::ToUTF8(appId).c_str(), Utils::ToUTF8(region).c_str())); + return std::make_shared(hlumodel); + } + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Language understanding model handle. + explicit LanguageUnderstandingModel(SPXLUMODELHANDLE hlumodel = SPXHANDLE_INVALID) : m_type(LanguageUnderstandingModelType::LanguageUnderstandingModel), m_hlumodel(hlumodel) { } + + /// + /// Virtual destructor. + /// + virtual ~LanguageUnderstandingModel() { language_understanding_model__handle_release(m_hlumodel); } + + /// + /// Internal. Explicit conversion operator. + /// + /// A handle. + explicit operator SPXLUMODELHANDLE() const { return m_hlumodel; } + + /// + /// Returns id for this model. + /// + /// An string representing the id of this model. + virtual SPXSTRING GetModelId() const { return Utils::ToSPXString(language_understanding_model_get_model_id(m_hlumodel)); } + + /// + /// Gets the model type. + /// + /// An enum representing the type of the model. + LanguageUnderstandingModelType GetModelType() const { return m_type; } +protected: + /// + /// Protected constructor for base classes to set type. + /// + /// Language understanding model type. + LanguageUnderstandingModel(LanguageUnderstandingModelType type) : m_type(type), m_hlumodel(SPXHANDLE_INVALID){} + + LanguageUnderstandingModelType m_type; +private: + DISABLE_COPY_AND_MOVE(LanguageUnderstandingModel); + + SPXLUMODELHANDLE m_hlumodel; +}; + + +} } } } // Microsoft::CognitiveServices::Speech::Intent diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_log_level.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_log_level.h new file mode 100644 index 0000000..ed130df --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_log_level.h @@ -0,0 +1,66 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/azai/vision/license for the full license information. +// + +#pragma once + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Diagnostics { +namespace Logging { + +/// +/// Defines the different available log levels. +/// +/// +/// This is used by different loggers to set the maximum level of detail they will output. +/// +/// +/// +/// +/// +/// +/// +enum class Level +{ + /// + /// Error logging level. Only errors will be logged. + /// + Error, + + /// + /// Warning logging level. Only errors and warnings will be logged. + /// + Warning, + + /// + /// Informational logging level. Only errors, warnings and informational log messages will be logged. + /// + Info, + + /// + /// Verbose logging level. All log messages will be logged. + /// + Verbose +}; + +/*! \cond INTERNAL */ +namespace Details +{ + inline const char * LevelToString(Level level) + { + switch (level) + { + case Level::Error: return "error"; + case Level::Warning: return "warning"; + case Level::Info: return "info"; + default: + case Level::Verbose: return "verbose"; + } + } +} +/*! \endcond */ + +}}}}} \ No newline at end of file diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_meeting.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_meeting.h new file mode 100644 index 0000000..1111e26 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_meeting.h @@ -0,0 +1,340 @@ + +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_meeting.h: Public API declarations for Meeting C++ class +// + +#pragma once +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Transcription { + +/// +/// Class for meeting. +/// +class Meeting : public std::enable_shared_from_this +{ +public: + + static constexpr size_t MAX_MEETING_ID_LEN = 1024; + + /// + /// Create a meeting using a speech config and a meeting id. + /// + /// A shared smart pointer of a speech config object. + /// meeting Id. + /// A shared smart pointer of the created meeting object. + static std::future> CreateMeetingAsync(std::shared_ptr speechConfig, const SPXSTRING& meetingId) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, meetingId.empty()); + auto future = std::async(std::launch::async, [meetingId, speechConfig]() -> std::shared_ptr { + SPXMEETINGHANDLE hmeeting; + SPX_THROW_ON_FAIL(meeting_create_from_config(&hmeeting, (SPXSPEECHCONFIGHANDLE)(*speechConfig), Utils::ToUTF8(meetingId).c_str())); + return std::make_shared(hmeeting); + }); + return future; + } + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Recognizer handle. + explicit Meeting(SPXMEETINGHANDLE hmeeting) : + m_hmeeting(hmeeting), + m_properties(hmeeting), + Properties(m_properties) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + } + + /// + /// Destructor. + /// + ~Meeting() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + ::meeting_release_handle(m_hmeeting); + m_hmeeting = SPXHANDLE_INVALID; + } + + /// + /// Internal operator used to get underlying handle value. + /// + /// A handle. + explicit operator SPXMEETINGHANDLE () const { return m_hmeeting; } + + /// + /// Get the meeting id. + /// + /// Meeting id. + SPXSTRING GetMeetingId() + { + char id[MAX_MEETING_ID_LEN + 1]; + std::memset(id, 0, MAX_MEETING_ID_LEN); + SPX_THROW_ON_FAIL(meeting_get_meeting_id(m_hmeeting, id, MAX_MEETING_ID_LEN)); + return id; + } + + /// + /// Add a participant to a meeting using the user's id. + /// + /// Note: The returned participant can be used to remove. If the client changes the participant's attributes, + /// the changed attributes are passed on to the service only when the participant is added again. + /// + /// A user id. + /// a shared smart pointer of the participant. + std::future> AddParticipantAsync(const SPXSTRING& userId) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this, userId]() -> std::shared_ptr { + const auto participant = Participant::From(userId); + SPX_THROW_ON_FAIL(meeting_update_participant(m_hmeeting, true, (SPXPARTICIPANTHANDLE)(*participant))); + return participant; + }); + return future; + } + + /// + /// Add a participant to a meeting using the User object. + /// + /// A shared smart pointer to a User object. + /// The passed in User object. + std::future> AddParticipantAsync(const std::shared_ptr& user) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this, user]() -> std::shared_ptr { + SPX_THROW_ON_FAIL(meeting_update_participant_by_user(m_hmeeting, true, (SPXUSERHANDLE)(*user))); + return user; + }); + return future; + } + + /// + /// Add a participant to a meeting using the participant object + /// + /// A shared smart pointer to a participant object. + /// The passed in participant object. + std::future> AddParticipantAsync(const std::shared_ptr& participant) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this, participant]() -> std::shared_ptr { + SPX_THROW_ON_FAIL(meeting_update_participant(m_hmeeting, true, (SPXPARTICIPANTHANDLE)(*participant))); + return participant; + }); + return future; + } + + /// + /// Remove a participant from a meeting using the participant object + /// + /// A shared smart pointer of a participant object. + /// An empty future. + std::future RemoveParticipantAsync(const std::shared_ptr& participant) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this, participant]() -> void { + SPX_THROW_ON_FAIL(meeting_update_participant(m_hmeeting, false, (SPXPARTICIPANTHANDLE)(*participant))); + }); + return future; + } + + /// + /// Remove a participant from a meeting using the User object + /// + /// A smart pointer of a User. + /// An empty future. + std::future RemoveParticipantAsync(const std::shared_ptr& user) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this, user]() -> void { + SPX_THROW_ON_FAIL(meeting_update_participant_by_user(m_hmeeting, false, SPXUSERHANDLE(*user))); + }); + return future; + } + + /// + /// Remove a participant from a meeting using a user id string. + /// + /// A user id. + /// An empty future. + std::future RemoveParticipantAsync(const SPXSTRING& userId) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this, userId]() -> void { + SPX_THROW_ON_FAIL(meeting_update_participant_by_user_id(m_hmeeting, false, Utils::ToUTF8(userId.c_str()))); + }); + return future; + } + + /// + /// Ends the current meeting. + /// + /// An empty future. + std::future EndMeetingAsync() + { + return RunAsync(::meeting_end_meeting); + } + + /// + /// Sets the authorization token that will be used for connecting the server. + /// + /// The authorization token. + void SetAuthorizationToken(const SPXSTRING& token) + { + Properties.SetProperty(PropertyId::SpeechServiceAuthorization_Token, token); + } + + /// + /// Gets the authorization token. + /// + /// Authorization token + SPXSTRING GetAuthorizationToken() + { + return Properties.GetProperty(PropertyId::SpeechServiceAuthorization_Token, SPXSTRING()); + } + + /// + /// Start the meeting. + /// + /// An empty future. + std::future StartMeetingAsync() + { + return RunAsync(::meeting_start_meeting); + } + + /// + /// Deletes the meeting. Any participants that are still part of the meeting + /// will be ejected after this call. + /// + /// An empty future. + std::future DeleteMeetingAsync() + { + return RunAsync(::meeting_delete_meeting); + } + + /// + /// Locks the meeting. After this no new participants will be able to join. + /// + /// An empty future. + std::future LockMeetingAsync() + { + return RunAsync(::meeting_lock_meeting); + } + + /// + /// Unlocks the meeting. + /// + /// An empty future. + std::future UnlockMeetingAsync() + { + return RunAsync(::meeting_unlock_meeting); + } + + /// + /// Mutes all participants except for the host. This prevents others from generating + /// transcriptions, or sending text messages. + /// + /// An empty future. + std::future MuteAllParticipantsAsync() + { + return RunAsync(::meeting_mute_all_participants); + } + + /// + /// Allows other participants to generate transcriptions, or send text messages. + /// + /// An empty future. + std::future UnmuteAllParticipantsAsync() + { + return RunAsync(::meeting_unmute_all_participants); + } + + /// + /// Mutes a particular participant. This will prevent them generating new transcriptions, + /// or sending text messages. + /// + /// The identifier for the participant. + /// An empty future. + std::future MuteParticipantAsync(const SPXSTRING& participantId) + { + return RunAsync([participantId = Utils::ToUTF8(participantId)](auto handle) + { + return ::meeting_mute_participant(handle, participantId.c_str()); + }); + } + + /// + /// Unmutes a particular participant. + /// + /// The identifier for the participant. + /// An empty future. + std::future UnmuteParticipantAsync(const SPXSTRING& participantId) + { + return RunAsync([participantId = Utils::ToUTF8(participantId)](auto handle) + { + return ::meeting_unmute_participant(handle, participantId.c_str()); + }); + } + +private: + + /*! \cond PRIVATE */ + + SPXMEETINGHANDLE m_hmeeting; + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXMEETINGHANDLE hmeeting) : + PropertyCollection( + [=]() { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + meeting_get_property_bag(hmeeting, &hpropbag); + return hpropbag; + }()) + { + } + }; + + PrivatePropertyCollection m_properties; + + inline std::future RunAsync(std::function func) + { + auto keepalive = this->shared_from_this(); + return std::async(std::launch::async, [keepalive, this, func]() + { + SPX_THROW_ON_FAIL(func(m_hmeeting)); + }); + } + + /*! \endcond */ + +public: + /// + /// A collection of properties and their values defined for this . + /// + PropertyCollection& Properties; + +}; + +}}}} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_meeting_transcriber.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_meeting_transcriber.h new file mode 100644 index 0000000..01609bf --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_meeting_transcriber.h @@ -0,0 +1,467 @@ + +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_meeting_transcriber.h: Public API declarations for MeetingTranscriber C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Transcription { + +class Session; + +/// +/// Class for meeting transcriber. +/// +class MeetingTranscriber : public Recognizer +{ +public: + + /// + /// Create a meeting transcriber from an audio config. + /// + /// Audio configuration. + /// A smart pointer wrapped meeting transcriber pointer. + static std::shared_ptr FromConfig(std::shared_ptr audioInput = nullptr) + { + SPXRECOHANDLE hreco = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::recognizer_create_meeting_transcriber_from_config( &hreco, + Utils::HandleOrInvalid(audioInput))); + + return std::make_shared(hreco); + } + + /// + /// Join a meeting. + /// + /// A smart pointer of the meeting to be joined. + /// An empty future. + std::future JoinMeetingAsync(std::shared_ptr meeting) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this, meeting]() -> void { + SPX_THROW_ON_FAIL(::recognizer_join_meeting(Utils::HandleOrInvalid(meeting), m_hreco)); + }); + + return future; + } + + /// + /// Leave a meeting. + /// + /// Note: After leaving a meeting, no transcribing or transcribed events will be sent to end users. End users need to join a meeting to get the events again. + /// + /// An empty future. + std::future LeaveMeetingAsync() + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this]() -> void { + SPX_THROW_ON_FAIL(::recognizer_leave_meeting(m_hreco)); + }); + + return future; + } + + /// + /// Asynchronously starts a meeting transcribing. + /// + /// An empty future. + std::future StartTranscribingAsync() + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this]() -> void { + SPX_INIT_HR(hr); + SPX_THROW_ON_FAIL(hr = recognizer_async_handle_release(m_hasyncStartContinuous)); // close any unfinished previous attempt + + SPX_EXITFN_ON_FAIL(hr = recognizer_start_continuous_recognition_async(m_hreco, &m_hasyncStartContinuous)); + SPX_EXITFN_ON_FAIL(hr = recognizer_start_continuous_recognition_async_wait_for(m_hasyncStartContinuous, UINT32_MAX)); + + SPX_EXITFN_CLEANUP: + auto releaseHr = recognizer_async_handle_release(m_hasyncStartContinuous); + SPX_REPORT_ON_FAIL(releaseHr); + m_hasyncStartContinuous = SPXHANDLE_INVALID; + + SPX_THROW_ON_FAIL(hr); + }); + + return future; + } + + /// + /// Asynchronously stops a meeting transcribing. + /// + /// An empty future. + std::future StopTranscribingAsync() + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this]() -> void { + + SPX_THROW_ON_FAIL(::recognizer_leave_meeting(m_hreco)); + + SPX_INIT_HR(hr); + SPX_THROW_ON_FAIL(hr = recognizer_async_handle_release(m_hasyncStopContinuous)); // close any unfinished previous attempt + + SPX_EXITFN_ON_FAIL(hr = recognizer_stop_continuous_recognition_async(m_hreco, &m_hasyncStopContinuous)); + SPX_EXITFN_ON_FAIL(hr = recognizer_stop_continuous_recognition_async_wait_for(m_hasyncStopContinuous, UINT32_MAX)); + + SPX_EXITFN_CLEANUP: + auto releaseHr = recognizer_async_handle_release(m_hasyncStopContinuous); + SPX_REPORT_ON_FAIL(releaseHr); + m_hasyncStopContinuous = SPXHANDLE_INVALID; + + SPX_THROW_ON_FAIL(hr); + }); + + return future; + } + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Recognizer handle. + explicit MeetingTranscriber(SPXRECOHANDLE hreco) throw() : + Recognizer(hreco), + SessionStarted(GetSessionEventConnectionsChangedCallback()), + SessionStopped(GetSessionEventConnectionsChangedCallback()), + SpeechStartDetected(GetRecognitionEventConnectionsChangedCallback()), + SpeechEndDetected(GetRecognitionEventConnectionsChangedCallback()), + Transcribing(GetRecoEventConnectionsChangedCallback()), + Transcribed(GetRecoEventConnectionsChangedCallback()), + Canceled(GetRecoCanceledEventConnectionsChangedCallback()), + m_hasyncStartContinuous(SPXHANDLE_INVALID), + m_hasyncStopContinuous(SPXHANDLE_INVALID), + m_properties(hreco), + Properties(m_properties) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + } + + /// + /// Destructor. + /// + ~MeetingTranscriber() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + TermRecognizer(); + } + + /// + /// Signal for events indicating the start of a recognition session (operation). + /// + EventSignal SessionStarted; + + /// + /// Signal for events indicating the end of a recognition session (operation). + /// + EventSignal SessionStopped; + + /// + /// Signal for events indicating the start of speech. + /// + EventSignal SpeechStartDetected; + + /// + /// Signal for events indicating the end of speech. + /// + EventSignal SpeechEndDetected; + + /// + /// Signal for events containing intermediate recognition results. + /// + EventSignal Transcribing; + + /// + /// Signal for events containing final recognition results. + /// (indicating a successful recognition attempt). + /// + EventSignal Transcribed; + + /// + /// Signal for events containing canceled recognition results + /// (indicating a recognition attempt that was canceled as a result or a direct cancellation request + /// or, alternatively, a transport or protocol failure). + /// + EventSignal Canceled; + + /// + /// Sets the authorization token that will be used for connecting the server. + /// + /// The authorization token. + void SetAuthorizationToken(const SPXSTRING& token) + { + Properties.SetProperty(PropertyId::SpeechServiceAuthorization_Token, token); + } + + /// + /// Gets the authorization token. + /// + /// Authorization token + SPXSTRING GetAuthorizationToken() + { + return Properties.GetProperty(PropertyId::SpeechServiceAuthorization_Token, SPXSTRING()); + } + +protected: + + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Recognizer handle. + virtual void TermRecognizer() override + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + // Disconnect the event signals in reverse construction order + Canceled.DisconnectAll(); + Transcribed.DisconnectAll(); + Transcribing.DisconnectAll(); + SpeechEndDetected.DisconnectAll(); + SpeechStartDetected.DisconnectAll(); + SessionStopped.DisconnectAll(); + SessionStarted.DisconnectAll(); + + // Close the async handles we have open for Recognize, StartContinuous, and StopContinuous + for (auto handle : { &m_hasyncStartContinuous, &m_hasyncStopContinuous }) + { + if (*handle != SPXHANDLE_INVALID && ::recognizer_async_handle_is_valid(*handle)) + { + ::recognizer_async_handle_release(*handle); + *handle = SPXHANDLE_INVALID; + } + } + + // Ask the base to term + Recognizer::TermRecognizer(); + } + + void RecoEventConnectionsChanged(const EventSignal& recoEvent) + { + if (m_hreco != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_hreco=0x%8p", __FUNCTION__, (void*)m_hreco); + SPX_DBG_TRACE_VERBOSE_IF(!::recognizer_handle_is_valid(m_hreco), "%s: m_hreco is INVALID!!!", __FUNCTION__); + + if (&recoEvent == &Transcribing) + { + recognizer_recognizing_set_callback(m_hreco, Transcribing.IsConnected() ? FireEvent_Transcribing : nullptr, this); + } + else if (&recoEvent == &Transcribed) + { + recognizer_recognized_set_callback(m_hreco, Transcribed.IsConnected() ? FireEvent_Transcribed : nullptr, this); + } + } + } + + static void FireEvent_Transcribing(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr recoEvent{ new MeetingTranscriptionEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->Transcribing.Signal(*recoEvent.get()); + } + + static void FireEvent_Transcribed(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr recoEvent{ new MeetingTranscriptionEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->Transcribed.Signal(*recoEvent.get()); + } + + void RecoCanceledEventConnectionsChanged(const EventSignal& recoEvent) + { + if (m_hreco != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_hreco=0x%8p", __FUNCTION__, (void*)m_hreco); + SPX_DBG_TRACE_VERBOSE_IF(!::recognizer_handle_is_valid(m_hreco), "%s: m_hreco is INVALID!!!", __FUNCTION__); + + if (&recoEvent == &Canceled) + { + recognizer_canceled_set_callback(m_hreco, Canceled.IsConnected() ? FireEvent_Canceled : nullptr, this); + } + } + } + + static void FireEvent_Canceled(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + + auto ptr = new MeetingTranscriptionCanceledEventArgs(hevent); + std::shared_ptr recoEvent(ptr); + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->Canceled.Signal(*ptr); + } + + void SessionEventConnectionsChanged(const EventSignal& sessionEvent) + { + if (m_hreco != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_hreco=0x%8p", __FUNCTION__, (void*)m_hreco); + SPX_DBG_TRACE_VERBOSE_IF(!::recognizer_handle_is_valid(m_hreco), "%s: m_hreco is INVALID!!!", __FUNCTION__); + + if (&sessionEvent == &SessionStarted) + { + recognizer_session_started_set_callback(m_hreco, SessionStarted.IsConnected() ? FireEvent_SessionStarted : nullptr, this); + } + else if (&sessionEvent == &SessionStopped) + { + recognizer_session_stopped_set_callback(m_hreco, SessionStopped.IsConnected() ? FireEvent_SessionStopped : nullptr, this); + } + } + } + + static void FireEvent_SessionStarted(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr sessionEvent{ new SessionEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->SessionStarted.Signal(*sessionEvent.get()); + + // SessionEventArgs doesn't hold hevent, and thus can't release it properly ... release it here + SPX_DBG_ASSERT(recognizer_event_handle_is_valid(hevent)); + recognizer_event_handle_release(hevent); + } + + static void FireEvent_SessionStopped(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr sessionEvent{ new SessionEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->SessionStopped.Signal(*sessionEvent.get()); + + // SessionEventArgs doesn't hold hevent, and thus can't release it properly ... release it here + SPX_DBG_ASSERT(recognizer_event_handle_is_valid(hevent)); + recognizer_event_handle_release(hevent); + } + + void RecognitionEventConnectionsChanged(const EventSignal& recognitionEvent) + { + if (m_hreco != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_hreco=0x%8p", __FUNCTION__, (void*)m_hreco); + SPX_DBG_TRACE_VERBOSE_IF(!::recognizer_handle_is_valid(m_hreco), "%s: m_hreco is INVALID!!!", __FUNCTION__); + + if (&recognitionEvent == &SpeechStartDetected) + { + recognizer_speech_start_detected_set_callback(m_hreco, SpeechStartDetected.IsConnected() ? FireEvent_SpeechStartDetected : nullptr, this); + } + else if (&recognitionEvent == &SpeechEndDetected) + { + recognizer_speech_end_detected_set_callback(m_hreco, SpeechEndDetected.IsConnected() ? FireEvent_SpeechEndDetected : nullptr, this); + } + } + } + + static void FireEvent_SpeechStartDetected(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr recoEvent{ new RecognitionEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->SpeechStartDetected.Signal(*recoEvent.get()); + + // RecognitionEventArgs doesn't hold hevent, and thus can't release it properly ... release it here + SPX_DBG_ASSERT(recognizer_event_handle_is_valid(hevent)); + recognizer_event_handle_release(hevent); + } + + static void FireEvent_SpeechEndDetected(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr recoEvent{ new RecognitionEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->SpeechEndDetected.Signal(*recoEvent.get()); + + // RecognitionEventArgs doesn't hold hevent, and thus can't release it properly ... release it here + SPX_DBG_ASSERT(recognizer_event_handle_is_valid(hevent)); + recognizer_event_handle_release(hevent); + } + + /*! \endcond */ + +private: + + SPXASYNCHANDLE m_hasyncStartContinuous; + SPXASYNCHANDLE m_hasyncStopContinuous; + + DISABLE_DEFAULT_CTORS(MeetingTranscriber); + friend class Microsoft::CognitiveServices::Speech::Session; + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXRECOHANDLE hreco) : + PropertyCollection( + [=]() { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + recognizer_get_property_bag(hreco, &hpropbag); + return hpropbag; + }()) + { + } + }; + + PrivatePropertyCollection m_properties; + + inline std::function&)> GetSessionEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& sessionEvent) { this->SessionEventConnectionsChanged(sessionEvent); }; + } + + inline std::function&)> GetRecoEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& recoEvent) { this->RecoEventConnectionsChanged(recoEvent); }; + } + + inline std::function&)> GetRecoCanceledEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& recoEvent) { this->RecoCanceledEventConnectionsChanged(recoEvent); }; + } + + inline std::function&)> GetRecognitionEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& recoEvent) { this->RecognitionEventConnectionsChanged(recoEvent); }; + } + +public: + /// + /// A collection of properties and their values defined for this . + /// + PropertyCollection& Properties; +}; + +} } } } // Microsoft::CognitiveServices::Speech::Transcription diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_meeting_transcription_eventargs.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_meeting_transcription_eventargs.h new file mode 100644 index 0000000..4ff1db1 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_meeting_transcription_eventargs.h @@ -0,0 +1,168 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_meeting_transcription_eventargs.h: Public API declarations for MeetingTranscriptionEventArgs C++ class +// + +#pragma once +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Transcription { + +/// +/// Class for meeting transcriber event arguments. +/// +class MeetingTranscriptionEventArgs : public RecognitionEventArgs +{ +private: + + SPXEVENTHANDLE m_hevent; + std::shared_ptr m_result; + +public: + + /// + /// Constructor. + /// + /// Event handle + explicit MeetingTranscriptionEventArgs(SPXEVENTHANDLE hevent) : + RecognitionEventArgs(hevent), + m_hevent(hevent), + m_result(std::make_shared(ResultHandleFromEventHandle(hevent))), + Result(m_result) + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hevent); + }; + + /// + virtual ~MeetingTranscriptionEventArgs() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hevent); + SPX_THROW_ON_FAIL(recognizer_event_handle_release(m_hevent)); + }; + +#if defined(BINDING_OBJECTIVE_C) +private: +#endif + /// + /// Meeting transcriber result. + /// + std::shared_ptr Result; + +#if defined(BINDING_OBJECTIVE_C) +public: +#else +protected: +#endif + + /*! \cond PROTECTED */ + + /// + /// Meeting transcriber result. + /// + std::shared_ptr GetResult() const { return m_result; } + + /*! \endcond */ + +private: + + DISABLE_DEFAULT_CTORS(MeetingTranscriptionEventArgs); + + SPXRESULTHANDLE ResultHandleFromEventHandle(SPXEVENTHANDLE hevent) + { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(recognizer_recognition_event_get_result(hevent, &hresult)); + return hresult; + } +}; + + +/// +/// Class for meeting transcriber canceled event arguments. +/// Added in version 1.5.0. +/// +class MeetingTranscriptionCanceledEventArgs : public MeetingTranscriptionEventArgs +{ +private: + + std::shared_ptr m_cancellation; + CancellationReason m_cancellationReason; + CancellationErrorCode m_errorCode; + +public: + + /// + /// Constructor. + /// + /// Event handle + explicit MeetingTranscriptionCanceledEventArgs(SPXEVENTHANDLE hevent) : + MeetingTranscriptionEventArgs(hevent), + m_cancellation(CancellationDetails::FromResult(GetResult())), + m_cancellationReason(m_cancellation->Reason), + m_errorCode(m_cancellation->ErrorCode), + Reason(m_cancellationReason), + ErrorCode(m_errorCode), + ErrorDetails(m_cancellation->ErrorDetails) + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p)", __FUNCTION__, (void*)this); + }; + + /// + virtual ~MeetingTranscriptionCanceledEventArgs() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p)", __FUNCTION__, (void*)this); + }; + +#if defined(BINDING_OBJECTIVE_C) +private: +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif + /// + /// The reason the result was canceled. + /// + const CancellationReason& Reason; + + /// + /// The error code in case of an unsuccessful recognition ( is set to Error). + /// If Reason is not Error, ErrorCode is set to NoError. + /// + const CancellationErrorCode& ErrorCode; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + /// + /// The error message in case of an unsuccessful recognition ( is set to Error). + /// + const SPXSTRING ErrorDetails; + +#if defined(BINDING_OBJECTIVE_C) +public: +#else +private: +#endif + /// + /// CancellationDetails. + /// + std::shared_ptr GetCancellationDetails() const { return m_cancellation; } + +private: + + DISABLE_DEFAULT_CTORS(MeetingTranscriptionCanceledEventArgs); +}; + + +} } } } // Microsoft::CognitiveServices::Speech::Transcription diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_meeting_transcription_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_meeting_transcription_result.h new file mode 100644 index 0000000..9b54fb8 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_meeting_transcription_result.h @@ -0,0 +1,96 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_meeting_transcriber_result.h: Public API declarations for MeetingTranscription C++ class +// + +#pragma once +#include +#include +#include +#include +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Transcription { + +/// +/// Represents the result of a meeting transcriber. +/// +class MeetingTranscriptionResult final : public RecognitionResult +{ +public: + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Result handle. + explicit MeetingTranscriptionResult(SPXRESULTHANDLE hresult) : + RecognitionResult(hresult), + UserId(m_userId), + UtteranceId(m_utteranceId) + { + PopulateSpeakerFields(hresult, &m_userId); + PopulateUtteranceFields(hresult, &m_utteranceId); + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p) -- resultid=%s; reason=0x%x; text=%s, userid=%s, utteranceid=%s", __FUNCTION__, (void*)this, (void*)Handle, Utils::ToUTF8(ResultId).c_str(), Reason, Utils::ToUTF8(Text).c_str(), Utils::ToUTF8(UserId).c_str(), Utils::ToUTF8(UtteranceId).c_str()); + } + + /// + /// Destructor. + /// + ~MeetingTranscriptionResult() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)Handle); + } + + /// + /// Unique Speaker id. + /// + const SPXSTRING& UserId; + + /// + /// Unique id that is consistent across all the intermediates and final speech recognition result from one user. + /// + const SPXSTRING& UtteranceId; + +private: + DISABLE_DEFAULT_CTORS(MeetingTranscriptionResult); + + void PopulateSpeakerFields(SPXRESULTHANDLE hresult, SPXSTRING* puserId) + { + SPX_INIT_HR(hr); + + const size_t maxCharCount = 1024; + char sz[maxCharCount + 1] = {}; + + if (puserId != nullptr && recognizer_result_handle_is_valid(hresult)) + { + SPX_THROW_ON_FAIL(hr = meeting_transcription_result_get_user_id(hresult, sz, maxCharCount)); + *puserId = Utils::ToSPXString(sz); + } + } + + void PopulateUtteranceFields(SPXRESULTHANDLE hresult, SPXSTRING* putteranceId) + { + SPX_INIT_HR(hr); + + const size_t maxCharCount = 1024; + char sz[maxCharCount + 1] = {}; + + if (putteranceId != nullptr && recognizer_result_handle_is_valid(hresult)) + { + SPX_THROW_ON_FAIL(hr = meeting_transcription_result_get_utterance_id(hresult, sz, maxCharCount)); + *putteranceId = Utils::ToSPXString(sz); + } + } + + SPXSTRING m_userId; + SPXSTRING m_utteranceId; +}; + + +} } } } // Microsoft::CognitiveServices::Speech::Transcription diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_memory_logger.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_memory_logger.h new file mode 100644 index 0000000..2e1644f --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_memory_logger.h @@ -0,0 +1,163 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Diagnostics { +namespace Logging { + +/// +/// Class with static methods to control SDK logging into an in-memory buffer. +/// Turning on logging while running your Speech SDK scenario provides +/// detailed information from the SDK's core native components. If you +/// report an issue to Microsoft, you may be asked to provide logs to help +/// Microsoft diagnose the issue. Your application should not take dependency +/// on particular log strings, as they may change from one SDK release to another +/// without notice. +/// MemoryLogger is designed for the case where you want to get access to logs +/// that were taken in the short duration before some unexpected event happens. +/// For example, if you are running a Speech Recognizer, you may want to dump the MemoryLogger +/// after getting an event indicating recognition was canceled due to some error. +/// The size of the memory buffer is fixed at 2MB and cannot be changed. This is +/// a "ring" buffer, that is, new log strings written replace the oldest ones +/// in the buffer. +/// Added in version 1.20.0 +/// +/// Memory logging is a process wide construct. That means that if (for example) +/// you have multiple speech recognizer objects running in parallel, there will be one +/// memory buffer containing interleaved logs from all recognizers. You cannot get a +/// separate logs for each recognizer. +class MemoryLogger +{ +public: + /// + /// Starts logging into the internal memory buffer. + /// + static void Start() + { + diagnostics_log_memory_start_logging(); + } + + /// + /// Stops logging into the internal memory buffer. + /// + static void Stop() + { + diagnostics_log_memory_stop_logging(); + } + + /// + /// Sets or clears filters for memory logging. + /// Once filters are set, memory logger will only be updated with log strings + /// containing at least one of the strings specified by the filters. The match is case sensitive. + /// + /// Optional. Filters to use, or an empty list to remove previously set filters. + static void SetFilters(std::initializer_list filters = {}) + { + std::string collapsedFilters = MemoryLogger::CollapseFilters(filters); + + diagnostics_log_memory_set_filters(collapsedFilters.c_str()); + } + + /// + /// Writes the content of the whole memory buffer to the specified file. + /// It does not block other SDK threads from continuing to log into the buffer. + /// + /// Path to a log file on local disk. + /// This does not reset (clear) the memory buffer. + static void Dump(const SPXSTRING& filePath) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, filePath.empty()); + + SPX_THROW_ON_FAIL(diagnostics_log_memory_dump(Utils::ToUTF8(filePath).c_str(), nullptr, false, false)); + } + + /// + /// Writes the content of the whole memory buffer to an object that implements std::ostream. + /// For example, std::cout (for console output). + /// It does not block other SDK threads from continuing to log into the buffer. + /// + /// std::ostream object to write to. + /// This does not reset (clear) the memory buffer. + static void Dump(std::ostream& outStream) + { + auto start = diagnostics_log_memory_get_line_num_oldest(); + auto stop = diagnostics_log_memory_get_line_num_newest(); + for (auto i = start; + i < stop; + i++) + { + const char* line = diagnostics_log_memory_get_line(i); + if (line) + { + outStream << line; + } + } + } + + /// + /// Returns the content of the whole memory buffer as a vector of strings. + /// It does not block other SDK threads from continuing to log into the buffer. + /// + /// A vector with the contents of the memory buffer copied into it. + /// This does not reset (clear) the memory buffer. + static std::vector Dump() + { + std::vector results; + + auto start = diagnostics_log_memory_get_line_num_oldest(); + auto stop = diagnostics_log_memory_get_line_num_newest(); + for (auto i = start; + i < stop; + i++) + { + const char* line = diagnostics_log_memory_get_line(i); + if (line) + { + results.push_back(line); + } + } + + return results; + } + + /// + /// Sets the level of the messages to be captured by the logger + /// + /// Maximum level of detail to be captured by the logger. + static void SetLevel(Level level) + { + const auto levelStr = Details::LevelToString(level); + diagnostics_set_log_level("memory", levelStr); + } + +private: + static std::string CollapseFilters(std::initializer_list filters) + { + std::string str = ""; + + if (filters.size() > 0) + { + std::ostringstream filtersCollapsed; + std::copy(filters.begin(), filters.end(), std::ostream_iterator(filtersCollapsed, ";")); + str = filtersCollapsed.str(); + } + + return str; + } +}; + +}}}}} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_participant.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_participant.h new file mode 100644 index 0000000..b87ee40 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_participant.h @@ -0,0 +1,222 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_participant.h: Public API declarations for Participant C++ class +// + +#pragma once +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Transcription { + +/// +/// Represents a participant in a conversation. +/// Added in version 1.5.0. +/// +class Participant +{ +private: + SPXPARTICIPANTHANDLE m_hparticipant; + SPXSTRING m_avatar; + SPXSTRING m_id; + SPXSTRING m_displayName; + bool m_isTts; + bool m_isMuted; + bool m_isHost; + +public: + /// + /// Create a participant using user id, her/his preferred language and her/his voice signature. + /// If voice signature is empty then user will not be identified. + /// + /// A user ids. + /// The preferred languages of the user. It can be optional. + /// The voice signature of the user. It can be optional. + /// A smart pointer of Participant + static std::shared_ptr From(const SPXSTRING& userId, const SPXSTRING& preferredLanguage = {}, const SPXSTRING& voiceSignature = {}) + { + SPXPARTICIPANTHANDLE hparticipant = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(participant_create_handle(&hparticipant, Utils::ToUTF8(userId.c_str()), Utils::ToUTF8(preferredLanguage.c_str()), Utils::ToUTF8(voiceSignature.c_str()))); + return std::make_shared(hparticipant); + } + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// participant handle. + explicit Participant(SPXPARTICIPANTHANDLE hparticipant = SPXHANDLE_INVALID) : + m_hparticipant(hparticipant), + m_avatar(), + m_id(), + m_displayName(), + m_isTts(false), + m_isMuted(false), + m_isHost(false), + Id(m_id), + Avatar(m_avatar), + DisplayName(m_displayName), + IsUsingTts(m_isTts), + IsMuted(m_isMuted), + IsHost(m_isHost), + m_properties(hparticipant), + Properties(m_properties) + { + LoadConversationParticipantProperties(hparticipant); + } + + /// + /// Virtual destructor. + /// + virtual ~Participant() { participant_release_handle(m_hparticipant); } + + /// + /// Get the identifier for the participant. + /// + const SPXSTRING& Id; + + /// + /// Gets the colour of the user's avatar as an HTML hex string (e.g. FF0000 for red). + /// + const SPXSTRING& Avatar; + + /// + /// The participant's display name. Please note that each participant within the same conversation must + /// have a different display name. Duplicate names within the same conversation are not allowed. You can + /// use the Id property as another way to refer to each participant. + /// + const SPXSTRING& DisplayName; + + /// + /// Gets whether or not the participant is using Text To Speech (TTS). + /// + const bool& IsUsingTts; + + /// + /// Gets whether or not this participant is muted. + /// + const bool& IsMuted; + + /// + /// Gets whether or not this participant is the host. + /// + const bool& IsHost; + + /// + /// Internal operator used to get underlying handle value. + /// + /// A handle. + explicit operator SPXPARTICIPANTHANDLE() const { return m_hparticipant; } + + /// + /// Set preferred language. + /// + /// The preferred language, such as "en-us". + void SetPreferredLanguage(const std::string& preferredLanguage) + { + SPX_THROW_ON_FAIL(participant_set_preferred_langugage(m_hparticipant, Utils::ToUTF8(preferredLanguage.c_str()))); + } + + /// + /// Set voice signature. + /// If voice signature is empty then user will not be identified. + /// + /// The participant's voice signature." + void SetVoiceSignature(const std::string& voiceSignature) + { + SPX_THROW_ON_FAIL(participant_set_voice_signature(m_hparticipant, Utils::ToUTF8(voiceSignature.c_str()))); + } + +private: + + /*! \cond PRIVATE */ + + DISABLE_COPY_AND_MOVE(Participant); + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXPARTICIPANTHANDLE hparticipant) : + PropertyCollection( + [=]() { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + participant_get_property_bag(hparticipant, &hpropbag); + return hpropbag; + }()) + { + } + }; + + PrivatePropertyCollection m_properties; + + SPXSTRING TryLoadString(SPXEVENTHANDLE hevent, SPXHR(SPXAPI_CALLTYPE * func)(SPXEVENTHANDLE, char*, uint32_t *)) + { + std::unique_ptr psz; + try + { + // query the string length + uint32_t length = 0; + + // don't use SPX_THROW_ON_FAIL since that creates a handle for exceptions that will leak + // since we don't care about them. + SPXHR hr = func(hevent, nullptr, &length); + if (SPX_FAILED(hr) || length == 0) + { + return SPXSTRING{}; + } + + psz = std::unique_ptr(new char[length]); + hr = func(hevent, psz.get(), &length); + if (SPX_FAILED(hr)) + { + return SPXSTRING{}; + } + + return Utils::ToSPXString(psz.get()); + } + catch (...) + { + // ignore errors since not all participants have the properties we need + return SPXSTRING{}; + } + } + + void LoadConversationParticipantProperties(SPXPARTICIPANTHANDLE hParticipant) + { + m_id = TryLoadString(hParticipant, conversation_translator_participant_get_id); + m_avatar = TryLoadString(hParticipant, conversation_translator_participant_get_avatar); + m_displayName = TryLoadString(hParticipant, conversation_translator_participant_get_displayname); + + bool val; + if (SPX_SUCCEEDED(conversation_translator_participant_get_is_using_tts(hParticipant, &val))) + { + m_isTts = val; + } + + if (SPX_SUCCEEDED(conversation_translator_participant_get_is_muted(hParticipant, &val))) + { + m_isMuted = val; + } + + if (SPX_SUCCEEDED(conversation_translator_participant_get_is_host(hParticipant, &val))) + { + m_isHost = val; + } + } + + /*! \endcond */ + +public: + + /// + /// Collection of additional participant properties. + /// + PropertyCollection& Properties; +}; + +}}}} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pattern_matching_entity.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pattern_matching_entity.h new file mode 100644 index 0000000..823518f --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pattern_matching_entity.h @@ -0,0 +1,46 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_pattern_matching_entity.h: Public API declarations for PatternMatchingEntity C++ struct +// + +#pragma once +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Intent { + +/// +/// Represents a pattern matching entity used for intent recognition. +/// +struct PatternMatchingEntity +{ + /// + /// An Id used to define this Entity if it is matched. This id must appear in an intent phrase + /// or it will never be matched. + /// + SPXSTRING Id; + + /// + /// The Type of this Entity. + /// + EntityType Type; + + /// + /// The EntityMatchMode of this Entity. + /// + EntityMatchMode Mode; + + /// + /// If the Type is List these phrases will be used as the list. + /// + std::vector Phrases; + +}; + +} } } } // Microsoft::CognitiveServices::Speech::Intent diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pattern_matching_intent.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pattern_matching_intent.h new file mode 100644 index 0000000..0670291 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pattern_matching_intent.h @@ -0,0 +1,36 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_pattern_matching_intent.h: Public API declarations for PatternMatchingIntent C++ struct +// + +#pragma once +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Intent { + +/// +/// Represents a pattern matching intent used for intent recognition. +/// +struct PatternMatchingIntent +{ + /// + /// Phrases and patterns that will trigger this intent. At least one phrase must exist to be able to + /// apply this intent to an IntentRecognizer. + /// + std::vector Phrases; + + /// + /// An Id used to define this Intent if it is matched. If no Id is specified, then the first phrase in Phrases + /// will be used. + /// + SPXSTRING Id; +}; + +} } } } // Microsoft::CognitiveServices::Speech::Intent diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pattern_matching_model.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pattern_matching_model.h new file mode 100644 index 0000000..2cb8954 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pattern_matching_model.h @@ -0,0 +1,372 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_pattern_matching_model.h: Public API declarations for PatternMatchingModel C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Intent { + + /// + /// Represents a pattern matching model used for intent recognition. + /// + class PatternMatchingModel : public LanguageUnderstandingModel + { + public: + + /// + /// Creates a pattern matching model using the specified model ID. + /// + /// A string that represents a unique Id for this model. + /// A shared pointer to pattern matching model. + static std::shared_ptr FromModelId(const SPXSTRING& modelId) + { + return std::shared_ptr { + new PatternMatchingModel(modelId) + }; + } + + /// + /// Creates a pattern matching model using the specified .json file. This should follow the Microsoft LUIS JSON export schema. + /// + /// A string that representing the path to a '.json' file. + /// A shared pointer to pattern matching model. + static std::shared_ptr FromJSONFile(const SPXSTRING& filepath) + { + FILE* fp; + int err; +#ifdef _MSC_VER + err = fopen_s(&fp, filepath.c_str(), "r"); +#else + fp = fopen(filepath.c_str(), "r"); + if (fp == NULL) + { + err = -1; + } + else + { + err = 0; + } +#endif + if (err == 0 && fp != NULL) + { + char buffer[1024] = {}; + size_t numread = 0; + std::string fileContents = ""; +#ifdef _MSC_VER + while ((numread = fread_s((void**)&buffer, sizeof(buffer), sizeof(char), sizeof(buffer), fp)) != 0) +#else + while ((numread = fread((void**)&buffer, sizeof(char), sizeof(buffer), fp)) != 0) +#endif + { + fileContents.append(buffer, numread); + } + fclose(fp); + return ParseJSONFile(fileContents); + } + else + { + SPX_TRACE_ERROR("Attempt to read %s failed.", SPXERR_FILE_OPEN_FAILED, filepath.c_str()); + return nullptr; + } + } + + /// + /// Creates a PatternMatchingModel using the specified istream pointing to an .json file in the LUIS json format. + /// This assumes the stream is already open and has permission to read. + /// + /// A stream that representing a '.json' file. + /// A shared pointer to pattern matching model. + static std::shared_ptr FromJSONFileStream(std::istream& iStream) + { + std::istreambuf_iterator iterator{iStream}; + std::string str(iterator, {}); + return ParseJSONFile(str); + } + + /// + /// Returns id for this model. + /// + /// A string representing the id of this model. + SPXSTRING GetModelId() const { return m_modelId; } + + /// + /// This container of Intents is used to define all the Intents this model will look for. + /// + std::vector Intents; + + /// + /// This container of Intents is used to define all the Intents this model will look for. + /// + std::vector Entities; + + private: + DISABLE_COPY_AND_MOVE(PatternMatchingModel); + + PatternMatchingModel(const SPXSTRING& modelId) : LanguageUnderstandingModel(LanguageUnderstandingModelType::PatternMatchingModel), m_modelId(modelId) {} + + SPXSTRING m_modelId; + + static std::shared_ptr ParseJSONFile(const std::string& fileContents) + { + auto model = std::shared_ptr(new PatternMatchingModel("")); + AZAC_HANDLE parserHandle; + auto root = ai_core_json_parser_create(&parserHandle, fileContents.c_str(), fileContents.size()); + if (!ai_core_json_parser_handle_is_valid(parserHandle)) + { + SPX_TRACE_ERROR("Attempt to parse language understanding json file failed.", SPXERR_UNSUPPORTED_FORMAT); + return nullptr; + } + int count = ai_core_json_item_count(parserHandle, root); + for (int i = 0; i < count; i++) + { + auto itemInt = ai_core_json_item_at(parserHandle, root, i, nullptr); + auto nameInt = ai_core_json_item_name(parserHandle, itemInt); + size_t nameSize; + auto name = ai_core_json_value_as_string_ptr(parserHandle, nameInt, &nameSize); + + size_t valueSize = 0; + auto value = ai_core_json_value_as_string_ptr(parserHandle, itemInt, &valueSize); + if (name != nullptr) + { + auto nameStr = std::string(name, nameSize); + if (nameStr == "luis_schema_version") + { + // We support any version that we are able to pull data out of. + } + else if (nameStr == "prebuiltEntities") + { + int prebuiltcount = ai_core_json_item_count(parserHandle, itemInt); + for (int j = 0; j < prebuiltcount; j++) + { + ParsePrebuiltEntityJson(parserHandle, model, itemInt, j); + } + } + else if (nameStr == "name") + { + model->m_modelId = std::string(value, valueSize); + } + else if (nameStr == "patternAnyEntities" || nameStr == "entities") + { + int anyCount = ai_core_json_item_count(parserHandle, itemInt); + for (int j = 0; j < anyCount; j++) + { + ParseEntityJson(parserHandle, model, itemInt, j); + } + } + else if (nameStr == "patterns") + { + int patternCount = ai_core_json_item_count(parserHandle, itemInt); + for (int j = 0; j < patternCount; j++) + { + ParsePatternJson(parserHandle, model, itemInt, j); + } + } + else if (nameStr == "closedLists") + { + int listCount = ai_core_json_item_count(parserHandle, itemInt); + for (int j = 0; j < listCount; j++) + { + ParseListEntityJson(parserHandle, model, itemInt, j); + } + } + } + } + return model; + } + + static void ParsePrebuiltEntityJson(AZAC_HANDLE parserHandle, std::shared_ptr model, int itemInt, int index) + { + auto subItemInt = ai_core_json_item_at(parserHandle, itemInt, index, nullptr); + int subItemCount = ai_core_json_item_count(parserHandle, subItemInt); + size_t nameSize = 0; + size_t valueSize = 0; + for (int subItemIndex = 0; subItemIndex < subItemCount; subItemIndex++) + { + auto prebuiltPairInt = ai_core_json_item_at(parserHandle, subItemInt, subItemIndex, nullptr); + auto nameInt = ai_core_json_item_name(parserHandle, prebuiltPairInt); + auto name = ai_core_json_value_as_string_ptr(parserHandle, nameInt, &nameSize); + if (name != nullptr) + { + auto nameStr = std::string(name, nameSize); + auto value = ai_core_json_value_as_string_ptr(parserHandle, prebuiltPairInt, &valueSize); + if (nameStr == "name" && value != nullptr) + { + auto valueStr = std::string(value, valueSize); + if (valueStr == "number") + { + model->Entities.push_back({ "number", EntityType::PrebuiltInteger, EntityMatchMode::Basic, {} }); + } + // ignore any other prebuilt types as they are not supported. + } + } + } + } + + static void ParseEntityJson(AZAC_HANDLE parserHandle, std::shared_ptr model, int itemInt, int index) + { + auto subItemInt = ai_core_json_item_at(parserHandle, itemInt, index, nullptr); + int subItemCount = ai_core_json_item_count(parserHandle, subItemInt); + size_t nameSize = 0; + size_t valueSize = 0; + for (int subItemIndex = 0; subItemIndex < subItemCount; subItemIndex++) + { + auto entityPairInt = ai_core_json_item_at(parserHandle, subItemInt, subItemIndex, nullptr); + auto nameInt = ai_core_json_item_name(parserHandle, entityPairInt); + auto name = ai_core_json_value_as_string_ptr(parserHandle, nameInt, &nameSize); + if (name != nullptr) + { + auto nameStr = std::string(name, nameSize); + auto value = ai_core_json_value_as_string_ptr(parserHandle, entityPairInt, &valueSize); + if (nameStr == "name" && value != nullptr) + { + model->Entities.push_back({ std::string(value, valueSize), EntityType::Any, EntityMatchMode::Basic, {}}); + } + // ignore any other pairs since we only care about the name. + } + } + } + + static void ParseListEntityJson(AZAC_HANDLE parserHandle, std::shared_ptr model, int itemInt, int index) + { + auto subItemInt = ai_core_json_item_at(parserHandle, itemInt, index, nullptr); + int subItemCount = ai_core_json_item_count(parserHandle, subItemInt); + size_t nameSize = 0; + size_t valueSize = 0; + // Default to Strict matching. + PatternMatchingEntity entity{ "", EntityType::List, EntityMatchMode::Strict, {} }; + for (int subItemIndex = 0; subItemIndex < subItemCount; subItemIndex++) + { + auto listPairInt = ai_core_json_item_at(parserHandle, subItemInt, subItemIndex, nullptr); + auto nameInt = ai_core_json_item_name(parserHandle, listPairInt); + auto name = ai_core_json_value_as_string_ptr(parserHandle, nameInt, &nameSize); + if (name != nullptr) + { + auto nameStr = std::string(name, nameSize); + if (nameStr == "name") + { + auto value = ai_core_json_value_as_string_ptr(parserHandle, listPairInt, &valueSize); + if (value != nullptr) + { + entity.Id = std::string(value, valueSize); + } + } + if (nameStr == "subLists") + { + ParseSubList(parserHandle, entity, listPairInt); + } + // ignore any other pairs since we only care about the name. + } + } + model->Entities.push_back(entity); + } + + static void ParseSubList(AZAC_HANDLE parserHandle, PatternMatchingEntity& entity, int listPairInt) + { + size_t nameSize = 0; + size_t valueSize = 0; + auto subListCount = ai_core_json_item_count(parserHandle, listPairInt); + for (int subListIndex = 0; subListIndex < subListCount; subListIndex++) + { + auto subListItemInt = ai_core_json_item_at(parserHandle, listPairInt, subListIndex, nullptr); + auto subListItemCount = ai_core_json_item_count(parserHandle, subListItemInt); + for (int subListItemIndex = 0; subListItemIndex < subListItemCount; subListItemIndex++) + { + auto subListPairInt = ai_core_json_item_at(parserHandle, subListItemInt, subListItemIndex, nullptr); + auto nameInt = ai_core_json_item_name(parserHandle, subListPairInt); + auto name = ai_core_json_value_as_string_ptr(parserHandle, nameInt, &nameSize); + if (name != nullptr) + { + auto nameStr = std::string(name, nameSize); + if (nameStr == "canonicalForm") + { + auto value = ai_core_json_value_as_string_ptr(parserHandle, subListPairInt, &valueSize); + if (value != nullptr) + { + entity.Phrases.push_back(std::string(value, valueSize)); + } + } + else if (nameStr == "list") + { + auto subListSynonymInt = ai_core_json_item_at(parserHandle, subListItemInt, subListItemIndex, nullptr); + auto subListSynonymItemCount = ai_core_json_item_count(parserHandle, subListSynonymInt); + for (int subListSynonymIndex = 0; subListSynonymIndex < subListSynonymItemCount; subListSynonymIndex++) + { + auto subListSynonymEntryInt = ai_core_json_item_at(parserHandle, subListSynonymInt, subListSynonymIndex, nullptr); + auto value = ai_core_json_value_as_string_ptr(parserHandle, subListSynonymEntryInt, &valueSize); + if (value != nullptr) + { + entity.Phrases.push_back(std::string(value, valueSize)); + } + } + } + } + } + } + } + + static void ParsePatternJson(AZAC_HANDLE parserHandle, std::shared_ptr model, int itemInt, int index) + { + auto subItemInt = ai_core_json_item_at(parserHandle, itemInt, index, nullptr); + int subItemCount = ai_core_json_item_count(parserHandle, subItemInt); + size_t nameSize = 0; + size_t valueSize = 0; + std::string patternStr, intentIdStr; + for (int subItemIndex = 0; subItemIndex < subItemCount; subItemIndex++) + { + auto entityPairInt = ai_core_json_item_at(parserHandle, subItemInt, subItemIndex, nullptr); + auto nameInt = ai_core_json_item_name(parserHandle, entityPairInt); + auto name = ai_core_json_value_as_string_ptr(parserHandle, nameInt, &nameSize); + if (name != nullptr) + { + auto nameStr = std::string(name, nameSize); + auto value = ai_core_json_value_as_string_ptr(parserHandle, entityPairInt, &valueSize); + if (value != nullptr) + { + if (nameStr == "pattern") + { + patternStr = std::string(value, valueSize); + } + else if (nameStr == "intent") + { + intentIdStr = std::string(value, valueSize); + } + } + // ignore any other pairs since we only care about the name. + } + } + if (!patternStr.empty() && !intentIdStr.empty()) + { + bool added = false; + for (auto& intent : model->Intents) + { + if (intent.Id == intentIdStr) + { + intent.Phrases.push_back(patternStr); + added = true; + break; + } + } + if (!added) + { + model->Intents.push_back({ {patternStr}, intentIdStr}); + } + } + } + +}; + +} } } } // Microsoft::CognitiveServices::Speech::Intent diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_phrase_list_grammar.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_phrase_list_grammar.h new file mode 100644 index 0000000..dbcece2 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_phrase_list_grammar.h @@ -0,0 +1,92 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_phrase_list_grammar.h: Public API declarations for PhraseListGrammar C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + + +/// +/// Represents a phrase list grammar for dynamic grammar scenarios. +/// Added in version 1.5.0. +/// +class PhraseListGrammar : public Grammar +{ +public: + + /// + /// Creates a phrase list grammar for the specified recognizer. + /// + /// The recognizer from which to obtain the phrase list grammar. + /// A shared pointer to phrase list grammar. + template + static std::shared_ptr FromRecognizer(std::shared_ptr recognizer) + { + return FromRecognizer(recognizer, Utils::ToSPXString(nullptr)); + } + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Phrase list grammar handle. + explicit PhraseListGrammar(SPXGRAMMARHANDLE hgrammar = SPXHANDLE_INVALID) : Grammar(hgrammar) { } + + /// + /// Adds a simple phrase that may be spoken by the user. + /// + /// The phrase to be added. + void AddPhrase(const SPXSTRING& text) + { + auto phrase = GrammarPhrase::From(text); + SPX_THROW_ON_FAIL(phrase_list_grammar_add_phrase(m_hgrammar.get(), (SPXPHRASEHANDLE)(*phrase.get()))); + } + + /// + /// Clears all phrases from the phrase list grammar. + /// + void Clear() + { + SPX_THROW_ON_FAIL(phrase_list_grammar_clear(m_hgrammar.get())); + } + +protected: + + /// + /// Internal. Creates a phrase list grammar for the specified recognizer, with the specified name. + /// + /// The recognizer from which to obtain the phrase list grammar. + /// The name of the phrase list grammar to create. + /// A shared pointer to phrase list grammar. + template + static std::shared_ptr FromRecognizer(std::shared_ptr recognizer, const SPXSTRING& name) + { + SPXRECOHANDLE hreco = recognizer != nullptr + ? (SPXRECOHANDLE)(*recognizer.get()) + : SPXHANDLE_INVALID; + + SPXGRAMMARHANDLE hgrammar = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(phrase_list_grammar_from_recognizer_by_name(&hgrammar, hreco, Utils::ToUTF8(name.c_str()))); + + return std::make_shared(hgrammar); + } + + +private: + + DISABLE_COPY_AND_MOVE(PhraseListGrammar); +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pronunciation_assessment_config.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pronunciation_assessment_config.h new file mode 100644 index 0000000..88ddc99 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pronunciation_assessment_config.h @@ -0,0 +1,222 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once + +#include + +#include "speechapi_cxx_properties.h" +#include "speechapi_cxx_string_helpers.h" +#include "speechapi_cxx_utils.h" +#include "speechapi_cxx_common.h" +#include "speechapi_cxx_enums.h" +#include +#include "speechapi_c_pronunciation_assessment_config.h" + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Class that defines pronunciation assessment configuration +/// Added in 1.14.0 +/// +class PronunciationAssessmentConfig +{ +public: + /// + /// Internal operator used to get underlying handle value. + /// + /// A handle. + explicit operator SPXPRONUNCIATIONASSESSMENTCONFIGHANDLE() const { return m_hconfig; } + + /// + /// Creates an instance of the PronunciationAssessmentConfig + /// For parameter details, see the table + /// [Pronunciation assessment parameters](/azure/cognitive-services/speech-service/rest-speech-to-text-short#pronunciation-assessment-parameters). + /// + /// The reference text + /// The point system for score calibration + /// The evaluation granularity + /// If enables miscue calculation. When true, the pronounced words are compared to the reference text, and are marked with omission/insertion based on the comparison; when false, the recognized text will always be reference text. + /// A shared pointer to the new PronunciationAssessmentConfig instance. + static std::shared_ptr Create(const std::string& referenceText, + PronunciationAssessmentGradingSystem gradingSystem = + PronunciationAssessmentGradingSystem::FivePoint, + PronunciationAssessmentGranularity granularity = + PronunciationAssessmentGranularity::Phoneme, + bool enableMiscue = false) + { + SPXPRONUNCIATIONASSESSMENTCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + + SPX_THROW_ON_FAIL( + create_pronunciation_assessment_config(&hconfig, Utils::ToUTF8(referenceText).c_str(), + static_cast(gradingSystem), + static_cast(granularity), + enableMiscue)); + const auto ptr = new PronunciationAssessmentConfig(hconfig); + return std::shared_ptr(ptr); + } + + /// + /// Creates an instance of the PronunciationAssessmentConfig + /// For parameters details, see the table + /// [Pronunciation assessment parameters](/azure/cognitive-services/speech-service/rest-speech-to-text-short#pronunciation-assessment-parameters). + /// + /// The reference text + /// The point system for score calibration + /// The evaluation granularity + /// If enables miscue calculation + /// A shared pointer to the new PronunciationAssessmentConfig instance. + static std::shared_ptr Create(const std::wstring& referenceText, + PronunciationAssessmentGradingSystem gradingSystem = + PronunciationAssessmentGradingSystem::FivePoint, + PronunciationAssessmentGranularity granularity = + PronunciationAssessmentGranularity::Phoneme, + bool enableMiscue = false) + { + return Create(Utils::ToUTF8(referenceText), gradingSystem, granularity, enableMiscue); + } + + /// + /// Creates an instance of the PronunciationAssessmentConfig from json. See the table + /// [Pronunciation assessment parameters](/azure/cognitive-services/speech-service/rest-speech-to-text-short#pronunciation-assessment-parameters). + /// + /// The json string containing the pronunciation assessment parameters. + /// A shared pointer to the new PronunciationAssessmentConfig instance. + static std::shared_ptr CreateFromJson(const SPXSTRING& json) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, json.empty()); + SPXAUTODETECTSOURCELANGCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + + SPX_THROW_ON_FAIL(create_pronunciation_assessment_config_from_json(&hconfig, Utils::ToUTF8(json).c_str())); + const auto ptr = new PronunciationAssessmentConfig(hconfig); + return std::shared_ptr(ptr); + } + + /// + /// Gets to json string of pronunciation assessment parameters. + /// + /// json string of pronunciation assessment parameters. + SPXSTRING ToJson() const + { + const char* jsonCch = pronunciation_assessment_config_to_json(m_hconfig); + return Utils::ToSPXString(Utils::CopyAndFreePropertyString(jsonCch)); + } + + /// + /// Gets the reference text. + /// + /// The reference text. + SPXSTRING GetReferenceText() + { + const char* value = property_bag_get_string(m_propertybag, static_cast(PropertyId::PronunciationAssessment_ReferenceText), nullptr, ""); + return Utils::ToSPXString(Utils::CopyAndFreePropertyString(value)); + } + + /// + /// Sets the reference text. + /// + /// The reference text. + void SetReferenceText(const std::string& referenceText) + { + property_bag_set_string(m_propertybag, static_cast(PropertyId::PronunciationAssessment_ReferenceText), nullptr, referenceText.c_str()); + } + + /// + /// Sets the reference text. + /// + /// The reference text. + void SetReferenceText(const std::wstring& referenceText) + { + SetReferenceText(Utils::ToUTF8(referenceText)); + } + + /// + /// Sets phoneme alphabet. Valid values are: "SAPI" (default) and "IPA". + /// + /// Added in version 1.20.0. + /// The phoneme alphabet. + void SetPhonemeAlphabet(const SPXSTRING& phonemeAlphabet) + { + property_bag_set_string(m_propertybag, static_cast(PropertyId::PronunciationAssessment_PhonemeAlphabet), nullptr, Utils::ToUTF8(phonemeAlphabet).c_str()); + } + + /// + /// Sets nbest phoneme count in the result. + /// + /// Added in version 1.20.0. + /// The nbest phoneme count. + void SetNBestPhonemeCount(int count) + { + property_bag_set_string(m_propertybag, static_cast(PropertyId::PronunciationAssessment_NBestPhonemeCount), nullptr, std::to_string(count).c_str()); + } + + /// + /// Enables prosody assessment. + /// + /// Added in version 1.33.0. + void EnableProsodyAssessment() + { + property_bag_set_string(m_propertybag, static_cast(PropertyId::PronunciationAssessment_EnableProsodyAssessment), nullptr, "true"); + } + + /// + /// Enables the content assessment with topic. + /// + /// Added in version 1.33.0. + /// The content topic. + void EnableContentAssessmentWithTopic(const SPXSTRING& contentTopic) + { + property_bag_set_string(m_propertybag, static_cast(PropertyId::PronunciationAssessment_ContentTopic), nullptr, Utils::ToUTF8(contentTopic).c_str()); + } + + /// + /// Applies the settings in this config to a Recognizer. + /// + /// The target Recognizer. + void ApplyTo(std::shared_ptr recognizer) const + { + SPX_INIT_HR(hr); + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, recognizer == nullptr); + + SPX_THROW_ON_FAIL(hr =::pronunciation_assessment_config_apply_to_recognizer(m_hconfig, recognizer->m_hreco)); + } + + /// + /// Destructs the object. + /// + virtual ~PronunciationAssessmentConfig() + { + pronunciation_assessment_config_release(m_hconfig); + property_bag_release(m_propertybag); + } + +private: + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + explicit PronunciationAssessmentConfig(SPXPRONUNCIATIONASSESSMENTCONFIGHANDLE hconfig) + :m_hconfig(hconfig) + { + SPX_THROW_ON_FAIL(pronunciation_assessment_config_get_property_bag(hconfig, &m_propertybag)); + } + + /// + /// Internal member variable that holds the config + /// + SPXPRONUNCIATIONASSESSMENTCONFIGHANDLE m_hconfig; + + /// + /// Internal member variable that holds the properties of the speech config + /// + SPXPROPERTYBAGHANDLE m_propertybag; + + DISABLE_COPY_AND_MOVE(PronunciationAssessmentConfig); +}; + +}}} + diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pronunciation_assessment_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pronunciation_assessment_result.h new file mode 100644 index 0000000..aedbcb7 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_pronunciation_assessment_result.h @@ -0,0 +1,142 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_pronunciation_assessment_result.h: Public API declarations for PronunciationAssessmentResult C++ class +// + +#pragma once +#include +#include +#include +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Class for content assessment results. +/// +class PronunciationContentAssessmentResult +{ +public: + /// + /// The score indicating the grammar of the given speech. + /// + const double GrammarScore; + + /// + /// The score indicating the vocabulary of the given speech. + /// + const double VocabularyScore; + + /// + /// The score indicating the topic of the given speech. + /// + const double TopicScore; + + /*! \cond INTERNAL */ + + PronunciationContentAssessmentResult(const PropertyCollection& properties) : + GrammarScore(std::stod(properties.GetProperty("ContentAssessment_GrammarScore", "-1"))), + VocabularyScore(std::stod(properties.GetProperty("ContentAssessment_VocabularyScore", "-1"))), + TopicScore(std::stod(properties.GetProperty("ContentAssessment_TopicScore", "-1"))) + { + } + + /*! \endcond */ + +}; + + +/// +/// Class for pronunciation assessment results. +/// +class PronunciationAssessmentResult +{ +public: + + /// + /// Creates a pronunciation assessment result object from recognition result + /// If nullptr is returned, it means the assessment is failed. + /// + /// recognition result + /// A shared pointer to the created PronunciationAssessmentResult instance. + static std::shared_ptr FromResult(std::shared_ptr result) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, result == nullptr); + if (result->Properties.GetProperty("AccuracyScore").empty() && result->Properties.GetProperty("ContentAssessment_GrammarScore").empty()) + { + return nullptr; + } + auto ptr = new PronunciationAssessmentResult(result->Properties); + return std::shared_ptr(ptr); + } + + /// + /// The score indicating the pronunciation accuracy of the given speech, which indicates + /// how closely the phonemes match a native speaker's pronunciation. + /// If this is less 0, it means the pronunciation assessment failed. + /// + const double AccuracyScore; + + /// + /// The overall score indicating the pronunciation quality of the given speech. + /// This is calculated from AccuracyScore, FluencyScore and CompletenessScore with weight. + /// If this is less 0, it means the pronunciation assessment failed. + /// + const double PronunciationScore; + + /// + /// The score indicating the completeness of the given speech by calculating the ratio of pronounced words towards entire input. + /// If this is less 0, it means the pronunciation assessment failed. + /// + const double CompletenessScore; + + /// + /// The score indicating the fluency of the given speech. + /// If this is less 0, it means the pronunciation assessment failed. + /// + const double FluencyScore; + + /// + /// The score indicating the prosody of the given speech. + /// If this is less 0, it means the prosody assessment is not enabled. + /// + const double ProsodyScore; + + /// + /// The content assessment result. Only available when content assessment is enabled. + /// + std::shared_ptr ContentAssessmentResult; + + +protected: + + /*! \cond PROTECTED */ + + + explicit PronunciationAssessmentResult(const PropertyCollection& properties) : + AccuracyScore(std::stod(properties.GetProperty("AccuracyScore", "-1"))), + PronunciationScore(std::stod(properties.GetProperty("PronScore", "-1"))), + CompletenessScore(std::stod(properties.GetProperty("CompletenessScore", "-1"))), + FluencyScore(std::stod(properties.GetProperty("FluencyScore", "-1"))), + ProsodyScore(std::stod(properties.GetProperty("ProsodyScore", "-1"))) + { + if (!properties.GetProperty("ContentAssessment_GrammarScore").empty()) + { + this->ContentAssessmentResult = std::make_shared(properties); + } + } + + /*! \endcond */ + +private: + + DISABLE_DEFAULT_CTORS(PronunciationAssessmentResult); +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_properties.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_properties.h new file mode 100644 index 0000000..5a8a7b8 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_properties.h @@ -0,0 +1,99 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +class KeywordRecognizer; + +/// +/// Class to retrieve or set a property value from a property collection. +/// +class PropertyCollection +{ +public: + + /// + /// Destructor. + /// + ~PropertyCollection() + { + if (property_bag_is_valid(m_propbag)) + { + property_bag_release(m_propbag); + m_propbag = SPXHANDLE_INVALID; + } + } + + /// + /// Set value of a property. + /// + /// The id of the property. See + /// value to set + void SetProperty(PropertyId propertyID, const SPXSTRING& value) + { + property_bag_set_string(m_propbag, (int)propertyID, NULL, Utils::ToUTF8(value).c_str()); + } + + /// + /// Set value of a property. + /// + /// The name of property. + /// value to set + void SetProperty(const SPXSTRING& propertyName, const SPXSTRING& value) + { + property_bag_set_string(m_propbag, -1, Utils::ToUTF8(propertyName).c_str(), Utils::ToUTF8(value).c_str()); + } + + /// + /// Returns value of a property. + /// If the property value is not defined, the specified default value is returned. + /// + /// The id of the property. See + /// The default value which is returned if no value is defined for the property (empty string by default). + /// value of the property. + SPXSTRING GetProperty(PropertyId propertyID, const SPXSTRING& defaultValue = SPXSTRING()) const + { + const char* propCch = property_bag_get_string(m_propbag, static_cast(propertyID), nullptr, Utils::ToUTF8(defaultValue).c_str()); + return Utils::ToSPXString(Utils::CopyAndFreePropertyString(propCch)); + } + + /// + /// Returns value of a property. + /// If the property value is not defined, the specified default value is returned. + /// + /// The name of the property. + /// The default value which is returned if no value is defined for the property (empty string by default). + /// value of the property. + SPXSTRING GetProperty(const SPXSTRING& propertyName, const SPXSTRING& defaultValue = SPXSTRING()) const + { + const char* propCch = property_bag_get_string(m_propbag, -1, Utils::ToUTF8(propertyName).c_str(), Utils::ToUTF8(defaultValue).c_str()); + return Utils::ToSPXString(Utils::CopyAndFreePropertyString(propCch)); + } + +protected: + friend class KeywordRecognizer; + + /*! \cond PROTECTED */ + + PropertyCollection(SPXPROPERTYBAGHANDLE propbag) : m_propbag(propbag) {} + + /*! \endcond */ + +private: + + DISABLE_COPY_AND_MOVE(PropertyCollection); + + SPXPROPERTYBAGHANDLE m_propbag; +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognition_async_recognizer.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognition_async_recognizer.h new file mode 100644 index 0000000..ebb63d4 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognition_async_recognizer.h @@ -0,0 +1,473 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_recognition_async_recognizer.h: Public API declarations for AsyncRecognizer C++ template class +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// AsyncRecognizer abstract base class. +/// +template +class AsyncRecognizer : public Recognizer +{ +public: + + /// + /// Performs recognition in a non-blocking (asynchronous) mode. + /// + /// Future containing result value (a shared pointer to RecoResult) + /// of the asynchronous recognition. + /// + virtual std::future> RecognizeOnceAsync() = 0; + + /// + /// Asynchronously initiates continuous recognition operation. + /// + /// An empty future. + virtual std::future StartContinuousRecognitionAsync() = 0; + + /// + /// Asynchronously terminates ongoing continuous recognition operation. + /// + /// An empty future. + virtual std::future StopContinuousRecognitionAsync() = 0; + + /// + /// Asynchronously initiates keyword recognition operation. + /// + /// The keyword recognition model that specifies the keyword to be recognized. + /// An asynchronous operation that starts the keyword recognition. + virtual std::future StartKeywordRecognitionAsync(std::shared_ptr model) = 0; + + /// + /// Asynchronously terminates ongoing keyword recognition operation. + /// + /// An empty future. + virtual std::future StopKeywordRecognitionAsync() = 0; + + /// + /// Signal for events indicating the start of a recognition session (operation). + /// + EventSignal SessionStarted; + + /// + /// Signal for events indicating the end of a recognition session (operation). + /// + EventSignal SessionStopped; + + /// + /// Signal for events indicating the start of speech. + /// + EventSignal SpeechStartDetected; + + /// + /// Signal for events indicating the end of speech. + /// + EventSignal SpeechEndDetected; + + /// + /// Signal for events containing intermediate recognition results. + /// + EventSignal Recognizing; + + /// + /// Signal for events containing final recognition results. + /// (indicating a successful recognition attempt). + /// + EventSignal Recognized; + + /// + /// Signal for events containing canceled recognition results + /// (indicating a recognition attempt that was canceled as a result or a direct cancellation request + /// or, alternatively, a transport or protocol failure). + /// + EventSignal Canceled; + +protected: + + /*! \cond PROTECTED */ + + explicit AsyncRecognizer(SPXRECOHANDLE hreco) throw() : + Recognizer(hreco), + SessionStarted(GetSessionEventConnectionsChangedCallback()), + SessionStopped(GetSessionEventConnectionsChangedCallback()), + SpeechStartDetected(GetRecognitionEventConnectionsChangedCallback()), + SpeechEndDetected(GetRecognitionEventConnectionsChangedCallback()), + Recognizing(GetRecoEventConnectionsChangedCallback()), + Recognized(GetRecoEventConnectionsChangedCallback()), + Canceled(GetRecoCanceledEventConnectionsChangedCallback()), + m_properties(hreco), + m_hasyncRecognize(SPXHANDLE_INVALID), + m_hasyncStartContinuous(SPXHANDLE_INVALID), + m_hasyncStopContinuous(SPXHANDLE_INVALID), + m_hasyncStartKeyword(SPXHANDLE_INVALID), + m_hasyncStopKeyword(SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + }; + + virtual ~AsyncRecognizer() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + TermRecognizer(); + }; + + virtual void TermRecognizer() override + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + // Disconnect the event signals in reverse construction order + Canceled.DisconnectAll(); + Recognized.DisconnectAll(); + Recognizing.DisconnectAll(); + SpeechEndDetected.DisconnectAll(); + SpeechStartDetected.DisconnectAll(); + SessionStopped.DisconnectAll(); + SessionStarted.DisconnectAll(); + + // Close the async handles we have open for Recognize, StartContinuous, and StopContinuous + for (auto handle : { &m_hasyncRecognize, &m_hasyncStartContinuous, &m_hasyncStopContinuous }) + { + if (*handle != SPXHANDLE_INVALID && ::recognizer_async_handle_is_valid(*handle)) + { + ::recognizer_async_handle_release(*handle); + *handle = SPXHANDLE_INVALID; + } + } + + // Ask the base to term + Recognizer::TermRecognizer(); + } + + std::future> RecognizeOnceAsyncInternal() + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this]() -> std::shared_ptr { + SPX_INIT_HR(hr); + + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(hr = recognizer_recognize_once(m_hreco, &hresult)); + + return std::make_shared(hresult); + }); + + return future; + }; + + std::future StartContinuousRecognitionAsyncInternal() + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this]() -> void { + SPX_INIT_HR(hr); + SPX_THROW_ON_FAIL(hr = recognizer_async_handle_release(m_hasyncStartContinuous)); // close any unfinished previous attempt + + SPX_EXITFN_ON_FAIL(hr = recognizer_start_continuous_recognition_async(m_hreco, &m_hasyncStartContinuous)); + SPX_EXITFN_ON_FAIL(hr = recognizer_start_continuous_recognition_async_wait_for(m_hasyncStartContinuous, UINT32_MAX)); + + SPX_EXITFN_CLEANUP: + auto releaseHr = recognizer_async_handle_release(m_hasyncStartContinuous); + SPX_REPORT_ON_FAIL(releaseHr); + m_hasyncStartContinuous = SPXHANDLE_INVALID; + + SPX_THROW_ON_FAIL(hr); + }); + + return future; + }; + + std::future StopContinuousRecognitionAsyncInternal() + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this]() -> void { + SPX_INIT_HR(hr); + SPX_THROW_ON_FAIL(hr = recognizer_async_handle_release(m_hasyncStopContinuous)); // close any unfinished previous attempt + + SPX_EXITFN_ON_FAIL(hr = recognizer_stop_continuous_recognition_async(m_hreco, &m_hasyncStopContinuous)); + SPX_EXITFN_ON_FAIL(hr = recognizer_stop_continuous_recognition_async_wait_for(m_hasyncStopContinuous, UINT32_MAX)); + + SPX_EXITFN_CLEANUP: + auto releaseHr = recognizer_async_handle_release(m_hasyncStopContinuous); + SPX_REPORT_ON_FAIL(releaseHr); + m_hasyncStopContinuous = SPXHANDLE_INVALID; + + SPX_THROW_ON_FAIL(hr); + }); + + return future; + } + + std::future StartKeywordRecognitionAsyncInternal(std::shared_ptr model) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, model, this]() -> void { + SPX_INIT_HR(hr); + SPX_THROW_ON_FAIL(hr = recognizer_async_handle_release(m_hasyncStartKeyword)); // close any unfinished previous attempt + + auto hkeyword = (SPXKEYWORDHANDLE)(*model.get()); + SPX_EXITFN_ON_FAIL(hr = recognizer_start_keyword_recognition_async(m_hreco, hkeyword, &m_hasyncStartKeyword)); + SPX_EXITFN_ON_FAIL(hr = recognizer_start_keyword_recognition_async_wait_for(m_hasyncStartKeyword, UINT32_MAX)); + + SPX_EXITFN_CLEANUP: + auto releaseHr = recognizer_async_handle_release(m_hasyncStartKeyword); + SPX_REPORT_ON_FAIL(releaseHr); + m_hasyncStartKeyword = SPXHANDLE_INVALID; + + SPX_THROW_ON_FAIL(hr); + }); + + return future; + }; + + std::future StopKeywordRecognitionAsyncInternal() + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [keepAlive, this]() -> void { + SPX_INIT_HR(hr); + SPX_THROW_ON_FAIL(hr = recognizer_async_handle_release(m_hasyncStopKeyword)); // close any unfinished previous attempt + + SPX_EXITFN_ON_FAIL(hr = recognizer_stop_keyword_recognition_async(m_hreco, &m_hasyncStopKeyword)); + SPX_EXITFN_ON_FAIL(hr = recognizer_stop_keyword_recognition_async_wait_for(m_hasyncStopKeyword, UINT32_MAX)); + + SPX_EXITFN_CLEANUP: + auto releaseHr = recognizer_async_handle_release(m_hasyncStopKeyword); + SPX_REPORT_ON_FAIL(releaseHr); + m_hasyncStartKeyword = SPXHANDLE_INVALID; + + SPX_THROW_ON_FAIL(hr); + }); + + return future; + }; + + virtual void RecoEventConnectionsChanged(const EventSignal& recoEvent) + { + if (m_hreco != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_hreco=0x%8p", __FUNCTION__, (void*)m_hreco); + SPX_DBG_TRACE_VERBOSE_IF(!::recognizer_handle_is_valid(m_hreco), "%s: m_hreco is INVALID!!!", __FUNCTION__); + + if (&recoEvent == &Recognizing) + { + recognizer_recognizing_set_callback(m_hreco, Recognizing.IsConnected() ? AsyncRecognizer::FireEvent_Recognizing: nullptr, this); + } + else if (&recoEvent == &Recognized) + { + recognizer_recognized_set_callback(m_hreco, Recognized.IsConnected() ? AsyncRecognizer::FireEvent_Recognized: nullptr, this); + } + } + } + + virtual void RecoCanceledEventConnectionsChanged(const EventSignal& recoEvent) + { + if (m_hreco != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_hreco=0x%8p", __FUNCTION__, (void*)m_hreco); + SPX_DBG_TRACE_VERBOSE_IF(!::recognizer_handle_is_valid(m_hreco), "%s: m_hreco is INVALID!!!", __FUNCTION__); + + if (&recoEvent == &Canceled) + { + recognizer_canceled_set_callback(m_hreco, Canceled.IsConnected() ? AsyncRecognizer::FireEvent_Canceled : nullptr, this); + } + } + } + + virtual void RecognitionEventConnectionsChanged(const EventSignal& recognitionEvent) + { + if (m_hreco != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_hreco=0x%8p", __FUNCTION__, (void*)m_hreco); + SPX_DBG_TRACE_VERBOSE_IF(!::recognizer_handle_is_valid(m_hreco), "%s: m_hreco is INVALID!!!", __FUNCTION__); + + if (&recognitionEvent == &SpeechStartDetected) + { + recognizer_speech_start_detected_set_callback(m_hreco, SpeechStartDetected.IsConnected() ? AsyncRecognizer::FireEvent_SpeechStartDetected : nullptr, this); + } + else if (&recognitionEvent == &SpeechEndDetected) + { + recognizer_speech_end_detected_set_callback(m_hreco, SpeechEndDetected.IsConnected() ? AsyncRecognizer::FireEvent_SpeechEndDetected : nullptr, this); + } + } + } + + virtual void SessionEventConnectionsChanged(const EventSignal& sessionEvent) + { + if (m_hreco != SPXHANDLE_INVALID) + { + SPX_DBG_TRACE_VERBOSE("%s: m_hreco=0x%8p", __FUNCTION__, (void*)m_hreco); + SPX_DBG_TRACE_VERBOSE_IF(!::recognizer_handle_is_valid(m_hreco), "%s: m_hreco is INVALID!!!", __FUNCTION__); + + if (&sessionEvent == &SessionStarted) + { + recognizer_session_started_set_callback(m_hreco, SessionStarted.IsConnected() ? AsyncRecognizer::FireEvent_SessionStarted: nullptr, this); + } + else if (&sessionEvent == &SessionStopped) + { + recognizer_session_stopped_set_callback(m_hreco, SessionStopped.IsConnected() ? AsyncRecognizer::FireEvent_SessionStopped : nullptr, this); + } + } + } + + static void FireEvent_SessionStarted(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr sessionEvent { new SessionEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->SessionStarted.Signal(*sessionEvent.get()); + + // SessionEventArgs doesn't hold hevent, and thus can't release it properly ... release it here + SPX_DBG_ASSERT(recognizer_event_handle_is_valid(hevent)); + recognizer_event_handle_release(hevent); + } + + static void FireEvent_SessionStopped(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr sessionEvent { new SessionEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->SessionStopped.Signal(*sessionEvent.get()); + + // SessionEventArgs doesn't hold hevent, and thus can't release it properly ... release it here + SPX_DBG_ASSERT(recognizer_event_handle_is_valid(hevent)); + recognizer_event_handle_release(hevent); + } + + static void FireEvent_SpeechStartDetected(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr recoEvent{ new RecognitionEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->SpeechStartDetected.Signal(*recoEvent.get()); + + // RecognitionEventArgs doesn't hold hevent, and thus can't release it properly ... release it here + SPX_DBG_ASSERT(recognizer_event_handle_is_valid(hevent)); + recognizer_event_handle_release(hevent); + } + + static void FireEvent_SpeechEndDetected(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr recoEvent{ new RecognitionEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->SpeechEndDetected.Signal(*recoEvent.get()); + + // RecognitionEventArgs doesn't hold hevent, and thus can't release it properly ... release it here + SPX_DBG_ASSERT(recognizer_event_handle_is_valid(hevent)); + recognizer_event_handle_release(hevent); + } + + static void FireEvent_Recognizing(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr recoEvent { new RecoEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->Recognizing.Signal(*recoEvent.get()); + } + + static void FireEvent_Recognized(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr recoEvent { new RecoEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->Recognized.Signal(*recoEvent.get()); + } + + static void FireEvent_Canceled(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + + auto ptr = new RecoCanceledEventArgs(hevent); + std::shared_ptr recoEvent(ptr); + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->Canceled.Signal(*ptr); + } + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXRECOHANDLE hreco) : + PropertyCollection( + [=](){ + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + recognizer_get_property_bag(hreco, &hpropbag); + return hpropbag; + }()) + { + } + }; + + PrivatePropertyCollection m_properties; + + SPXASYNCHANDLE m_hasyncRecognize; + SPXASYNCHANDLE m_hasyncStartContinuous; + SPXASYNCHANDLE m_hasyncStopContinuous; + SPXASYNCHANDLE m_hasyncStartKeyword; + SPXASYNCHANDLE m_hasyncStopKeyword; + + template + static Handle HandleOrInvalid(std::shared_ptr audioInput) + { + return audioInput == nullptr + ? (Handle)SPXHANDLE_INVALID + : (Handle)(*audioInput.get()); + } + + /*! \endcond */ + +private: + + DISABLE_DEFAULT_CTORS(AsyncRecognizer); + + inline std::function&)> GetSessionEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& sessionEvent) { this->SessionEventConnectionsChanged(sessionEvent); }; + } + + inline std::function&)> GetRecoEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& recoEvent) { this->RecoEventConnectionsChanged(recoEvent); }; + } + + inline std::function&)> GetRecoCanceledEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& recoEvent) { this->RecoCanceledEventConnectionsChanged(recoEvent); }; + } + + inline std::function&)> GetRecognitionEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& recoEvent) { this->RecognitionEventConnectionsChanged(recoEvent); }; + } +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognition_base_async_recognizer.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognition_base_async_recognizer.h new file mode 100644 index 0000000..5723e46 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognition_base_async_recognizer.h @@ -0,0 +1,53 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_recognition_base_async_recognizer.h: Public API declarations for BaseAsyncRecognizer C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// BaseAsyncRecognizer class. +/// +class BaseAsyncRecognizer : public AsyncRecognizer +{ +protected: + + /*! \cond PROTECTED */ + + using BaseType = AsyncRecognizer; + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + explicit BaseAsyncRecognizer(SPXRECOHANDLE hreco) : + BaseType(hreco) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + } + + ~BaseAsyncRecognizer() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + TermRecognizer(); + } + + DISABLE_DEFAULT_CTORS(BaseAsyncRecognizer); + + /*! \endcond */ +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognition_eventargs.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognition_eventargs.h new file mode 100644 index 0000000..59713c8 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognition_eventargs.h @@ -0,0 +1,68 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_recognition_eventargs.h: Public API declarations for RecognitionEventArgs C++ base class +// + +#pragma once +#include +#include +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + + +/// +/// Provides data for the RecognitionEvent. +/// +class RecognitionEventArgs : public SessionEventArgs +{ +public: + + /// + /// Constructor. Creates a new instance using the provided handle. + /// + /// Event handle. + explicit RecognitionEventArgs(SPXEVENTHANDLE hevent) : + SessionEventArgs(hevent), + Offset(m_offset), + m_offset(GetOffset(hevent)) + { + }; + + /// + virtual ~RecognitionEventArgs() {} + + /// + /// The offset of recognition event + /// + const uint64_t& Offset; + +protected: + + /*! \cond PROTECTED */ + + /// + /// Extract offset from given event handle + /// + static uint64_t GetOffset(SPXEVENTHANDLE hevent) + { + uint64_t offset = 0; + SPX_THROW_ON_FAIL(recognizer_recognition_event_get_offset(hevent, &offset)); + return offset; + } + + /*! \endcond */ + +private: + + DISABLE_COPY_AND_MOVE(RecognitionEventArgs); + uint64_t m_offset; +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognition_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognition_result.h new file mode 100644 index 0000000..f8b8f6e --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognition_result.h @@ -0,0 +1,310 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_recognition_result.h: Public API declarations for RecognitionResult C++ base class and related enum class +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + + +/// +/// Contains detailed information about result of a recognition operation. +/// +class RecognitionResult +{ +private: + + /*! \cond PRIVATE */ + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXRESULTHANDLE hresult) : + PropertyCollection( + [=]() { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + result_get_property_bag(hresult, &hpropbag); + return hpropbag; + }()) + { + } + }; + + PrivatePropertyCollection m_properties; + + /*! \endcond */ + +public: + + /// + /// Virtual destructor. + /// + virtual ~RecognitionResult() + { + ::recognizer_result_handle_release(m_hresult); + m_hresult = SPXHANDLE_INVALID; + }; + + /// + /// Unique result id. + /// + const SPXSTRING& ResultId; + + /// + /// Recognition reason. + /// + const Speech::ResultReason& Reason; + + /// + /// Normalized text generated by a speech recognition engine from recognized input. + /// + const SPXSTRING& Text; + + /// + /// Duration of recognized speech in ticks. + /// A single tick represents one hundred nanoseconds or one ten-millionth of a second. + /// + /// Duration of recognized speech in ticks. + uint64_t Duration() const { return m_duration; } + + /// + /// Offset of the recognized speech in ticks. + /// A single tick represents one hundred nanoseconds or one ten-millionth of a second. + /// + /// Offset of the recognized speech in ticks. + uint64_t Offset() const { return m_offset; } + + /// + /// Collection of additional RecognitionResult properties. + /// + const PropertyCollection& Properties; + + /// + /// Internal. Explicit conversion operator. + /// + /// A handle. + explicit operator SPXRESULTHANDLE() { return m_hresult; } + + +protected: + + /*! \cond PROTECTED */ + + explicit RecognitionResult(SPXRESULTHANDLE hresult) : + m_properties(hresult), + ResultId(m_resultId), + Reason(m_reason), + Text(m_text), + Properties(m_properties), + Handle(m_hresult), + m_hresult(hresult) + { + PopulateResultFields(hresult, &m_resultId, &m_reason, &m_text); + } + + const SPXRESULTHANDLE& Handle; + + /*! \endcond */ + +private: + + DISABLE_DEFAULT_CTORS(RecognitionResult); + + void PopulateResultFields(SPXRESULTHANDLE hresult, SPXSTRING* resultId, Speech::ResultReason* reason, SPXSTRING* text) + { + + SPX_INIT_HR(hr); + + const size_t maxCharCount = 2048; + char sz[maxCharCount + 1] = {}; + + if (resultId != nullptr) + { + SPX_THROW_ON_FAIL(hr = result_get_result_id(hresult, sz, maxCharCount)); + *resultId = Utils::ToSPXString(sz); + } + + if (reason != nullptr) + { + Result_Reason resultReason; + SPX_THROW_ON_FAIL(hr = result_get_reason(hresult, &resultReason)); + *reason = (Speech::ResultReason)resultReason; + } + + if (text != nullptr) + { + SPX_THROW_ON_FAIL(hr = result_get_text(hresult, sz, maxCharCount)); + *text = Utils::ToSPXString(sz); + } + + SPX_THROW_ON_FAIL(hr = result_get_offset(hresult, &m_offset)); + SPX_THROW_ON_FAIL(hr = result_get_duration(hresult, &m_duration)); + } + + SPXRESULTHANDLE m_hresult; + + SPXSTRING m_resultId; + Speech::ResultReason m_reason; + SPXSTRING m_text; + uint64_t m_offset; + uint64_t m_duration; +}; + + +/// +/// Contains detailed information about why a result was canceled. +/// +class CancellationDetails +{ +private: + + CancellationReason m_reason; + CancellationErrorCode m_errorCode; + +public: + + /// + /// Creates an instance of CancellationDetails object for the canceled RecognitionResult. + /// + /// The result that was canceled. + /// A shared pointer to CancellationDetails. + static std::shared_ptr FromResult(std::shared_ptr result) + { + // VSTS 1407221 + // SPX_THROW_HR_IF(result->Reason != ResultReason::Canceled, SPXERR_INVALID_ARG); + auto ptr = new CancellationDetails(result.get()); + auto cancellation = std::shared_ptr(ptr); + return cancellation; + } + + /// + /// The reason the result was canceled. + /// + const CancellationReason& Reason; + + /// + /// The error code in case of an unsuccessful recognition ( is set to Error). + /// If Reason is not Error, ErrorCode is set to NoError. + /// Added in version 1.1.0. + /// + const CancellationErrorCode& ErrorCode; + + /// + /// The error message in case of an unsuccessful recognition ( is set to Error). + /// + const SPXSTRING ErrorDetails; + +protected: + + /*! \cond PROTECTED */ + + CancellationDetails(RecognitionResult* result) : + m_reason(GetCancellationReason(result)), + m_errorCode(GetCancellationErrorCode(result)), + Reason(m_reason), + ErrorCode(m_errorCode), + ErrorDetails(result->Properties.GetProperty(PropertyId::SpeechServiceResponse_JsonErrorDetails)) + { + } + + /*! \endcond */ + +private: + + DISABLE_DEFAULT_CTORS(CancellationDetails); + + Speech::CancellationReason GetCancellationReason(RecognitionResult* result) + { + Result_CancellationReason reason = CancellationReason_Error; + + SPXRESULTHANDLE hresult = (SPXRESULTHANDLE)(*result); + SPX_IFFAILED_THROW_HR(result_get_reason_canceled(hresult, &reason)); + + return (Speech::CancellationReason)reason; + } + + Speech::CancellationErrorCode GetCancellationErrorCode(RecognitionResult* result) + { + Result_CancellationErrorCode errorCode = CancellationErrorCode_NoError; + + SPXRESULTHANDLE hresult = (SPXRESULTHANDLE)(*result); + SPX_IFFAILED_THROW_HR(result_get_canceled_error_code(hresult, &errorCode)); + + return (Speech::CancellationErrorCode)errorCode; + } + +}; + + +/// +/// Contains detailed information for NoMatch recognition results. +/// +class NoMatchDetails +{ +private: + + NoMatchReason m_reason; + +public: + + /// + /// Creates an instance of NoMatchDetails object for NoMatch RecognitionResults. + /// + /// The recognition result that was not recognized. + /// A shared pointer to NoMatchDetails. + static std::shared_ptr FromResult(std::shared_ptr result) + { + // VSTS 1407221 + // SPX_THROW_HR_IF(SPXERR_INVALID_ARG, result->Reason != ResultReason::NoMatch); + auto ptr = new NoMatchDetails(result.get()); + auto noMatch = std::shared_ptr(ptr); + return noMatch; + } + + /// + /// The reason the result was not recognized. + /// + const NoMatchReason& Reason; + +protected: + + /*! \cond PROTECTED */ + + NoMatchDetails(RecognitionResult* result) : + m_reason(GetNoMatchReason(result)), + Reason(m_reason) + { + } + + /*! \endcond */ + +private: + + DISABLE_DEFAULT_CTORS(NoMatchDetails); + + Speech::NoMatchReason GetNoMatchReason(RecognitionResult* result) + { + Result_NoMatchReason reason = NoMatchReason_NotRecognized; + + SPXRESULTHANDLE hresult = (SPXRESULTHANDLE)(*result); + SPX_IFFAILED_THROW_HR(result_get_no_match_reason(hresult, &reason)); + + return (Speech::NoMatchReason)reason; + } + +}; + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognizer.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognizer.h new file mode 100644 index 0000000..a63e451 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_recognizer.h @@ -0,0 +1,72 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_recognizer.h: Public API declarations for Recognizer C++ base class +// + +#pragma once +#include +#include +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Recognizer base class. +/// +class Recognizer : public std::enable_shared_from_this +{ + friend class Connection; + friend class PronunciationAssessmentConfig; +public: + + /// + /// Internal operator used to get underlying handle value. + /// + /// A handle. + explicit operator SPXRECOHANDLE() const { return m_hreco; } + +protected: + + /*! \cond PROTECTED */ + + explicit Recognizer(SPXRECOHANDLE hreco) : + m_hreco(hreco) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + SPX_DBG_TRACE_VERBOSE("%s: m_hreco=0x%8p", __FUNCTION__, (void*)m_hreco); + } + + virtual ~Recognizer() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + TermRecognizer(); + } + + virtual void TermRecognizer() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + if (m_hreco != SPXHANDLE_INVALID) + { + ::recognizer_handle_release(m_hreco); + m_hreco = SPXHANDLE_INVALID; + SPX_DBG_TRACE_VERBOSE("%s: m_hreco=0x%8p", __FUNCTION__, (void*)m_hreco); + } + } + + SPXRECOHANDLE m_hreco; + + /*! \endcond */ + +private: + + DISABLE_DEFAULT_CTORS(Recognizer); +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_session.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_session.h new file mode 100644 index 0000000..92c6c38 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_session.h @@ -0,0 +1,86 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_session.h: Public API declarations for Session C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/*! \cond PRIVATE */ + +class Session +{ +private: + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXSESSIONHANDLE hsession) : + PropertyCollection( + [=](){ + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + session_get_property_bag(hsession, &hpropbag); + return hpropbag; + }()) + { + } + }; + + PrivatePropertyCollection m_properties; + +public: + + template + static std::shared_ptr FromRecognizer(std::shared_ptr recognizer) + { + SPX_INIT_HR(hr); + + SPXSESSIONHANDLE hsession = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(hr = ::session_from_recognizer(recognizer->m_hreco, &hsession)); + + return std::make_shared(hsession); + } + + explicit Session(SPXSESSIONHANDLE hsession) : + m_properties(hsession), + Properties(m_properties), + m_hsession(hsession) + { + SPX_DBG_TRACE_FUNCTION(); + } + + virtual ~Session() + { + SPX_DBG_TRACE_FUNCTION(); + + if (m_hsession != SPXHANDLE_INVALID) + { + ::session_handle_release(m_hsession); + m_hsession = SPXHANDLE_INVALID; + } + } + + PropertyCollection& Properties; + +private: + + DISABLE_COPY_AND_MOVE(Session); + + SPXSESSIONHANDLE m_hsession; +}; + +/*! \endcond */ + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_session_eventargs.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_session_eventargs.h new file mode 100644 index 0000000..117de20 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_session_eventargs.h @@ -0,0 +1,73 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_session_eventargs.h: Public API declarations for SessionEventArgs C++ class +// + +#pragma once +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + + +/// +/// Base class for session event arguments. +/// +class SessionEventArgs : public EventArgs +{ +public: + + /// + /// Constructor. + /// + /// Event handle + explicit SessionEventArgs(SPXEVENTHANDLE hevent) : + SessionId(m_sessionId), + m_sessionId(GetSessionId(hevent)) + { + }; + + /// + virtual ~SessionEventArgs() {} + + /// + /// Session identifier (a GUID in string format). + /// + const SPXSTRING& SessionId; + + +protected: + + /*! \cond PROTECTED */ + + /// + /// Extract session identifier from given event handle + /// + static const SPXSTRING GetSessionId(SPXEVENTHANDLE hevent) + { + static const auto cchMaxUUID = 36; + static const auto cchMaxSessionId = cchMaxUUID + 1; + char sessionId[cchMaxSessionId] = {}; + + SPX_THROW_ON_FAIL(recognizer_session_event_get_session_id(hevent, sessionId, cchMaxSessionId)); + return Utils::ToSPXString(sessionId); + }; + + /*! \endcond */ + +private: + + DISABLE_DEFAULT_CTORS(SessionEventArgs); + + SPXSTRING m_sessionId; +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_smart_handle.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_smart_handle.h new file mode 100644 index 0000000..d4727b9 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_smart_handle.h @@ -0,0 +1,60 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_smart_handle.h: Public API declarations for SmartHandle class and related typedef +// + +#pragma once +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + + +typedef SPXHR(SPXAPI_CALLTYPE *SmartHandleCloseFunction)(SPXHANDLE); + + +/// +/// Smart handle class. +/// +template +class SmartHandle +{ +public: + + SmartHandle(T handle = SPXHANDLE_INVALID) : m_handle(handle) { }; + ~SmartHandle() { reset(); } + + explicit operator T&() const { return m_handle; } + + T get() const { return m_handle; } + operator T() const { return m_handle; } + + T* operator &() + { + SPX_THROW_HR_IF(SPXERR_ALREADY_INITIALIZED, !InvalidHandle(m_handle)); + return &m_handle; + } + + void reset() + { + if (!InvalidHandle(m_handle)) + { + closeFunction(m_handle); + m_handle = SPXHANDLE_INVALID; + } + } + +private: + + static bool InvalidHandle(T t) { return t == nullptr || t == SPXHANDLE_INVALID; } + + DISABLE_COPY_AND_MOVE(SmartHandle); + T m_handle; +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_source_lang_config.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_source_lang_config.h new file mode 100644 index 0000000..a312525 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_source_lang_config.h @@ -0,0 +1,91 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once + +#include +#include +#include + +#include "speechapi_cxx_properties.h" +#include +#include +#include "speechapi_c_common.h" + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Class that defines source language configuration, added in 1.8.0 +/// +class SourceLanguageConfig +{ +public: + /// + /// Internal operator used to get underlying handle value. + /// + /// A handle. + explicit operator SPXSOURCELANGCONFIGHANDLE() const { return m_hconfig; } + + /// + /// Creates an instance of the SourceLanguageConfig with source language + /// + /// The source language + /// A shared pointer to the new SourceLanguageConfig instance. + static std::shared_ptr FromLanguage(const SPXSTRING& language) + { + SPXSOURCELANGCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(source_lang_config_from_language(&hconfig, language.c_str())); + auto ptr = new SourceLanguageConfig(hconfig); + return std::shared_ptr(ptr); + } + + /// + /// Creates an instance of the SourceLanguageConfig with source language and custom endpoint id. A custom endpoint id corresponds to custom models. + /// + /// The source language + /// The custom endpoint id + /// A shared pointer to the new SourceLanguageConfig instance. + static std::shared_ptr FromLanguage(const SPXSTRING& language, const SPXSTRING& endpointId) + { + SPXSOURCELANGCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(source_lang_config_from_language_and_endpointId(&hconfig, language.c_str(), endpointId.c_str())); + auto ptr = new SourceLanguageConfig(hconfig); + return std::shared_ptr(ptr); + } + + /// + /// Destructs the object. + /// + virtual ~SourceLanguageConfig() + { + source_lang_config_release(m_hconfig); + property_bag_release(m_propertybag); + } + +private: + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + explicit SourceLanguageConfig(SPXSOURCELANGCONFIGHANDLE hconfig) + :m_hconfig(hconfig) + { + SPX_THROW_ON_FAIL(source_lang_config_get_property_bag(hconfig, &m_propertybag)); + } + + /// + /// Internal member variable that holds the config + /// + SPXSOURCELANGCONFIGHANDLE m_hconfig; + + /// + /// Internal member variable that holds the properties of the config + /// + SPXPROPERTYBAGHANDLE m_propertybag; + + DISABLE_COPY_AND_MOVE(SourceLanguageConfig); +}; +}}} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_source_language_recognizer.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_source_language_recognizer.h new file mode 100644 index 0000000..36b851a --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_source_language_recognizer.h @@ -0,0 +1,173 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_source_language_recognizer.h: Public API declarations for SourceLanguageRecognizer C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +class Session; + +/// +/// Class for source language recognizers. +/// You can use this class for standalone language detection. +/// Added in version 1.17.0 +/// +class SourceLanguageRecognizer final : public AsyncRecognizer +{ +public: + + using BaseType = AsyncRecognizer; + + /// + /// Create a source language recognizer from a speech config, auto detection source language config and audio config + /// + /// Speech configuration + /// Auto detection source language config + /// Audio configuration + /// A smart pointer wrapped source language recognizer pointer. + static std::shared_ptr FromConfig( + std::shared_ptr speechconfig, + std::shared_ptr autoDetectSourceLangConfig, + std::shared_ptr audioInput = nullptr) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_source_language_recognizer_from_auto_detect_source_lang_config( + &hreco, + HandleOrInvalid(speechconfig), + HandleOrInvalid(autoDetectSourceLangConfig), + HandleOrInvalid(audioInput))); + return std::make_shared(hreco); + } + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Recognizer handle. + explicit SourceLanguageRecognizer(SPXRECOHANDLE hreco) : BaseType(hreco), Properties(m_properties) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + } + + /// + /// Destructor. + /// + ~SourceLanguageRecognizer() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + TermRecognizer(); + } + + /// + /// Starts speech recognition, and returns after a single utterance is recognized. The end of a + /// single utterance is determined by listening for silence at the end or until a maximum of about 30 + /// seconds of audio is processed. The task returns the recognition text as result. + /// Note: Since RecognizeOnceAsync() returns only a single utterance, it is suitable only for single + /// shot recognition like command or query. + /// For long-running multi-utterance recognition, use StartContinuousRecognitionAsync() instead. + /// + /// Future containing result value (a shared pointer to SpeechRecognitionResult) + /// of the asynchronous speech recognition. + /// + std::future> RecognizeOnceAsync() override + { + return BaseType::RecognizeOnceAsyncInternal(); + } + + /// + /// Asynchronously initiates continuous speech recognition operation. + /// + /// An empty future. + std::future StartContinuousRecognitionAsync() override + { + return BaseType::StartContinuousRecognitionAsyncInternal(); + } + + /// + /// Asynchronously terminates ongoing continuous speech recognition operation. + /// + /// An empty future. + std::future StopContinuousRecognitionAsync() override + { + return BaseType::StopContinuousRecognitionAsyncInternal(); + } + + /// + /// Asynchronously initiates keyword recognition operation. + /// + /// Specifies the keyword model to be used. + /// An empty future. + std::future StartKeywordRecognitionAsync(std::shared_ptr model) override + { + return BaseType::StartKeywordRecognitionAsyncInternal(model); + } + + /// + /// Asynchronously terminates keyword recognition operation. + /// + /// An empty future. + std::future StopKeywordRecognitionAsync() override + { + return BaseType::StopKeywordRecognitionAsyncInternal(); + } + + /// + /// A collection of properties and their values defined for this . + /// + PropertyCollection& Properties; + + /// + /// Gets the endpoint ID of a customized speech model that is used for speech recognition. + /// + /// the endpoint ID of a customized speech model that is used for speech recognition + SPXSTRING GetEndpointId() + { + return Properties.GetProperty(PropertyId::SpeechServiceConnection_EndpointId, SPXSTRING()); + } + + /// + /// Sets the authorization token that will be used for connecting to the service. + /// Note: The caller needs to ensure that the authorization token is valid. Before the authorization token + /// expires, the caller needs to refresh it by calling this setter with a new valid token. + /// Otherwise, the recognizer will encounter errors during recognition. + /// + /// The authorization token. + void SetAuthorizationToken(const SPXSTRING& token) + { + Properties.SetProperty(PropertyId::SpeechServiceAuthorization_Token, token); + } + + /// + /// Gets the authorization token. + /// + /// Authorization token + SPXSTRING GetAuthorizationToken() + { + return Properties.GetProperty(PropertyId::SpeechServiceAuthorization_Token, SPXSTRING()); + } + +private: + DISABLE_DEFAULT_CTORS(SourceLanguageRecognizer); + friend class Microsoft::CognitiveServices::Speech::Session; +}; +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speaker_identification_model.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speaker_identification_model.h new file mode 100644 index 0000000..786e3e7 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speaker_identification_model.h @@ -0,0 +1,77 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_speaker_identification_model.h: Public API declarations for SpeakerIdentificationModel C++ class +// + +#pragma once +#include +#include +#include + +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Speaker { + +/// +/// Represents speaker identification model used with speaker recognition class. +/// Added in version 1.12.0 +/// +class SpeakerIdentificationModel : public std::enable_shared_from_this +{ +public: + + /// + /// Creates a speaker identification model using the voice profiles. + /// + /// a vector of voice profiles. + /// A shared pointer to speaker identification model. + static std::shared_ptr FromProfiles(const std::vector>& profiles) + { + SPXSIMODELHANDLE hsimodel = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(speaker_identification_model_create(&hsimodel)); + for (auto& profile : profiles) + { + SPX_THROW_ON_FAIL(speaker_identification_model_add_profile(hsimodel, (SPXVOICEPROFILEHANDLE)(*profile))); + } + + return std::shared_ptr{ new SpeakerIdentificationModel(hsimodel) }; + } + + /// + /// Virtual destructor. + /// + virtual ~SpeakerIdentificationModel() { speaker_identification_model_release_handle(m_simodel); } + + /// + /// Internal. Explicit conversion operator. + /// + /// A handle. + explicit operator SPXSIMODELHANDLE() { return m_simodel; } + +protected: + + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a speaker identification model using the provided handle. + /// + /// speaker identification handle. + explicit SpeakerIdentificationModel(SPXSIMODELHANDLE hsimodel = SPXHANDLE_INVALID) : m_simodel(hsimodel) { } + + + /*! \endcond */ + +private: + + DISABLE_COPY_AND_MOVE(SpeakerIdentificationModel); + + SPXSIMODELHANDLE m_simodel; +}; + +} } } } // Microsoft::CognitiveServices::Speech::Speaker diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speaker_recognition_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speaker_recognition_result.h new file mode 100644 index 0000000..d37e643 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speaker_recognition_result.h @@ -0,0 +1,236 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_speaker_recognition_result.h: Public API declarations for SpeakerRecognitionResult C++ class +// + +#pragma once +#include +#include +#include + +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Speaker { + +/// +/// Represents speaker recognition result. +/// Added in 1.12.0 +/// +class SpeakerRecognitionResult +{ + +private: + + /// Internal member variable that holds the speakerRecognition result handle. + /// + SPXRESULTHANDLE m_hresult; + + /*! \cond PRIVATE */ + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXRESULTHANDLE hresult) : + PropertyCollection( + [hresult]() { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + result_get_property_bag(hresult, &hpropbag); + return hpropbag; + }()) + { + } + }; + + /// + /// Internal member variable that holds the properties associating to the speaker recognition result. + /// + PrivatePropertyCollection m_properties; + + /*! \endcond */ + +public: + + /// + /// Creates a new instance using the provided handle. + /// + /// Result handle. + explicit SpeakerRecognitionResult(SPXRESULTHANDLE hresult) : + m_hresult(hresult), + m_properties(hresult), + ResultId(m_resultId), + Reason(m_reason), + ProfileId(m_profileId), + Properties(m_properties), + m_profileId(Properties.GetProperty("speakerrecognition.profileid","")), + m_score(std::stof(Properties.GetProperty("speakerrecognition.score", "0.0"))) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + PopulateResultFields(hresult, &m_resultId, &m_reason); + } + + /// + /// Explicit conversion operator. + /// + /// A handle. + explicit operator SPXRESULTHANDLE() { return m_hresult; } + + /// + /// Destructor. + /// + ~SpeakerRecognitionResult() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + recognizer_result_handle_release(m_hresult); + } + + /// + /// Unique result id. + /// + const SPXSTRING& ResultId; + + /// + /// Reason of the speaker recognition result. + /// + const ResultReason& Reason; + + /// + /// The profile id of the first verified/identified speaker. The rest of recognized speakers can be retrieved by parsing the json result string in the Properties. + /// + const SPXSTRING& ProfileId; + + /// + /// Collection of additional properties. + /// + const PropertyCollection& Properties; + + /// + /// Returns a similarity score. + /// + /// A float number indicating the similarity between input audio and targeted voice profile.This number is between 0 and 1. A higher number means higher similarity.< / returns> + double GetScore() const + { + return m_score; + } + +private: + + /*! \cond PRIVATE */ + + DISABLE_DEFAULT_CTORS(SpeakerRecognitionResult); + + void PopulateResultFields(SPXRESULTHANDLE hresult, SPXSTRING* resultId, Speech::ResultReason* reason) + { + SPX_INIT_HR(hr); + + const size_t maxCharCount = 2048; + char sz[maxCharCount + 1] = {}; + + if (resultId != nullptr) + { + SPX_THROW_ON_FAIL(hr = result_get_result_id(hresult, sz, maxCharCount)); + *resultId = Utils::ToSPXString(sz); + } + + if (reason != nullptr) + { + Result_Reason resultReason; + SPX_THROW_ON_FAIL(hr = result_get_reason(hresult, &resultReason)); + *reason = (Speech::ResultReason)resultReason; + } + } + + SPXSTRING m_resultId; + ResultReason m_reason; + SPXSTRING m_profileId; + float m_score; + + /*! \endcond */ +}; + +/// +/// Represents the details of a canceled speaker recognition result. +/// +class SpeakerRecognitionCancellationDetails +{ +private: + + CancellationReason m_reason; + CancellationErrorCode m_errorCode; + +public: + + /// + /// Creates an instance of SpeakerRecognitionCancellationDetails object for the canceled speaker recognition result. + /// + /// The result that was canceled. + /// A shared pointer to SpeakerRecognitionCancellationDetails. + static std::shared_ptr FromResult(std::shared_ptr result) + { + return std::shared_ptr { new SpeakerRecognitionCancellationDetails(result.get()) }; + } + + /// + /// The reason the result was canceled. + /// + const CancellationReason& Reason; + + /// + /// The error code in case of an unsuccessful speaker recognition ( is set to Error). + /// If Reason is not Error, ErrorCode is set to NoError. + /// + const CancellationErrorCode& ErrorCode; + + /// + /// The error message in case of an unsuccessful speaker recognition ( is set to Error). + /// + const SPXSTRING ErrorDetails; + +protected: + + /*! \cond PROTECTED */ + + SpeakerRecognitionCancellationDetails(SpeakerRecognitionResult* result) : + m_reason(GetCancellationReason(result)), + m_errorCode(GetCancellationErrorCode(result)), + Reason(m_reason), + ErrorCode(m_errorCode), + ErrorDetails(result->Properties.GetProperty(PropertyId::SpeechServiceResponse_JsonErrorDetails)) + { + } + + /*! \endcond */ + +private: + + DISABLE_DEFAULT_CTORS(SpeakerRecognitionCancellationDetails); + + + CancellationReason GetCancellationReason(SpeakerRecognitionResult* result) + { + Result_CancellationReason reason = CancellationReason_Error; + + SPXRESULTHANDLE hresult = (SPXRESULTHANDLE)(*result); + SPX_IFFAILED_THROW_HR(result_get_reason_canceled(hresult, &reason)); + + return static_cast(reason); + } + + CancellationErrorCode GetCancellationErrorCode(SpeakerRecognitionResult* result) + { + Result_CancellationErrorCode errorCode = CancellationErrorCode_NoError; + + SPXRESULTHANDLE hresult = (SPXRESULTHANDLE)(*result); + SPX_IFFAILED_THROW_HR(result_get_canceled_error_code(hresult, &errorCode)); + + return static_cast(errorCode); + } +}; + +} } } } // Microsoft::CognitiveServices::Speech::Speaker diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speaker_recognizer.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speaker_recognizer.h new file mode 100644 index 0000000..6922573 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speaker_recognizer.h @@ -0,0 +1,142 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_speaker_recognizer.h: Public API declarations for speaker recognizer C++ class +// + +#pragma once +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Speaker { + +/// +/// Perform speaker recognition. +/// Added in version 1.12.0 +/// +class SpeakerRecognizer : public std::enable_shared_from_this +{ +public: + + /// + /// Create a speaker recognizer from a speech config and audio config. + /// + /// A shared smart pointer of a speech config. + /// A shared smart pointer of a audio config. + /// A smart pointer wrapped speaker recognizer pointer. + static std::shared_ptr FromConfig(std::shared_ptr speechconfig, std::shared_ptr audioInput) + { + SPXSPEAKERIDHANDLE hSpeakerRecognizerHandle; + SPX_THROW_ON_FAIL(::recognizer_create_speaker_recognizer_from_config( + &hSpeakerRecognizerHandle, + Utils::HandleOrInvalid(speechconfig), + Utils::HandleOrInvalid(audioInput))); + return std::shared_ptr{ new SpeakerRecognizer(hSpeakerRecognizerHandle) }; + } + + /// + /// Destructor. + /// + virtual ~SpeakerRecognizer() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + ::speaker_recognizer_release_handle(m_hSpeakerRecognizer); + m_hSpeakerRecognizer = SPXHANDLE_INVALID; + } + + /// + /// Verify the speaker in the verification model. + /// + /// A shared smart pointer of a speaker verficiation model. + /// A smart pointer wrapped speaker recognition result future. + std::future> RecognizeOnceAsync(std::shared_ptr model) + { + return RunAsync(speaker_recognizer_verify, model); + } + + /// + /// Identify the speakers in the Speaker Identification Model. + /// + /// A shared smart pointer of a speaker identification model. + /// A smart pointer wrapped speaker recognition result future. + std::future> RecognizeOnceAsync(std::shared_ptr model) + { + return RunAsync(speaker_recognizer_identify, model); + } + +protected: + + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// A Speaker Recognizer handle. + explicit SpeakerRecognizer(SPXSPEAKERIDHANDLE hSpeakerRecognizer) : + m_hSpeakerRecognizer(hSpeakerRecognizer), + m_properties(hSpeakerRecognizer), + Properties(m_properties) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + } + + /*! \endcond */ + +private: + + /*! \cond PRIVATE */ + + SPXSPEAKERIDHANDLE m_hSpeakerRecognizer; + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXSPEAKERIDHANDLE hSpeakerRecognizer) : + PropertyCollection( + [=]() { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + speaker_recognizer_get_property_bag(hSpeakerRecognizer, &hpropbag); + return hpropbag; + }()) + { + } + }; + + PrivatePropertyCollection m_properties; + + template < class SpeakerModelPtrType, class SpeakerModelHandleType> + inline std::future> RunAsync(std::function func, std::shared_ptr model) + { + auto keepalive = this->shared_from_this(); + return std::async(std::launch::async, [keepalive, this, func, model]() + { + SPXRESULTHANDLE hResultHandle = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(func(m_hSpeakerRecognizer, (SpeakerModelHandleType)(*model), &hResultHandle)); + return std::shared_ptr { new SpeakerRecognitionResult{ hResultHandle } }; + }); + } + + /*! \endcond */ + +public: + + /// + /// A collection of properties and their values defined for this . + /// + PropertyCollection& Properties; +}; + +} } } } // Microsoft::CognitiveServices::Speech::Speaker diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speaker_verification_model.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speaker_verification_model.h new file mode 100644 index 0000000..14ffd59 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speaker_verification_model.h @@ -0,0 +1,71 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_speaker_verification_model.h: Public API declarations for SpeakerVerificationModel C++ class +// + +#pragma once +#include +#include + +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Speaker { + +/// +/// Represents speaker verification model used with speaker recognition class. +/// Added in version 1.12.0 +/// +class SpeakerVerificationModel : public std::enable_shared_from_this +{ +public: + + /// + /// Creates a speaker verification model using the voice profile. + /// + /// The voice profile. + /// A shared pointer to speaker verification model. + static std::shared_ptr FromProfile(std::shared_ptr profile) + { + SPXSVMODELHANDLE hsvmodel = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(speaker_verification_model_create(&hsvmodel, (SPXVOICEPROFILEHANDLE)(*profile))); + return std::shared_ptr{ new SpeakerVerificationModel(hsvmodel) }; + } + + /// + /// Virtual destructor. + /// + virtual ~SpeakerVerificationModel() { speaker_verification_model_release_handle(m_svmodel); } + + /// + /// Internal. Explicit conversion operator. + /// + /// A handle. + explicit operator SPXSVMODELHANDLE() { return m_svmodel; } + +protected: + + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance of speaker verification model using the provided handle. + /// + /// speaker verification model handle. + explicit SpeakerVerificationModel(SPXSIMODELHANDLE hsvmodel = SPXHANDLE_INVALID) : m_svmodel(hsvmodel) { } + + /*! \endcond */ + +private: + + DISABLE_COPY_AND_MOVE(SpeakerVerificationModel); + + SPXSVMODELHANDLE m_svmodel; +}; + +} } } } // Microsoft::CognitiveServices::Speech::Speaker diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_config.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_config.h new file mode 100644 index 0000000..b2f9db7 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_config.h @@ -0,0 +1,491 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_speech_config.h: Public API declarations for SpeechConfig C++ class +// +#pragma once + +#include + +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +namespace Dialog { class DialogServiceConfig; } +class EmbeddedSpeechConfig; +class HybridSpeechConfig; + +/// +/// Class that defines configurations for speech / intent recognition, or speech synthesis. +/// +class SpeechConfig +{ +public: + friend Dialog::DialogServiceConfig; + friend EmbeddedSpeechConfig; + friend HybridSpeechConfig; + + /// + /// Internal operator used to get underlying handle value. + /// + /// A handle. + explicit operator SPXSPEECHCONFIGHANDLE() const { return m_hconfig; } + + /// + /// Creates an instance of the speech config with specified subscription key and region. + /// + /// The subscription key. + /// The region name (see the region page). + /// A shared pointer to the new speech config instance. + static std::shared_ptr FromSubscription(const SPXSTRING& subscription, const SPXSTRING& region) + { + SPXSPEECHCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(speech_config_from_subscription(&hconfig, Utils::ToUTF8(subscription).c_str(), Utils::ToUTF8(region).c_str())); + + auto ptr = new SpeechConfig(hconfig); + return std::shared_ptr(ptr); + } + + /// + /// Creates an instance of the speech config with specified authorization token and region. + /// Note: The caller needs to ensure that the authorization token is valid. Before the authorization token + /// expires, the caller needs to refresh it by calling this setter with a new valid token. + /// As configuration values are copied when creating a new recognizer, the new token value will not apply to recognizers that have already been created. + /// For recognizers that have been created before, you need to set authorization token of the corresponding recognizer + /// to refresh the token. Otherwise, the recognizers will encounter errors during recognition. + /// + /// The authorization token. + /// The region name (see the region page). + /// A shared pointer to the new speech config instance. + static std::shared_ptr FromAuthorizationToken(const SPXSTRING& authToken, const SPXSTRING& region) + { + SPXSPEECHCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(speech_config_from_authorization_token(&hconfig, Utils::ToUTF8(authToken).c_str(), Utils::ToUTF8(region).c_str())); + + auto ptr = new SpeechConfig(hconfig); + return std::shared_ptr(ptr); + } + + /// + /// Creates an instance of the speech config with specified endpoint and subscription. + /// This method is intended only for users who use a non-standard service endpoint. + /// Note: The query parameters specified in the endpoint URI are not changed, even if they are set by any other APIs. + /// For example, if the recognition language is defined in URI as query parameter "language=de-DE", and also set by SetSpeechRecognitionLanguage("en-US"), + /// the language setting in URI takes precedence, and the effective language is "de-DE". + /// Only the parameters that are not specified in the endpoint URI can be set by other APIs. + /// Note: To use an authorization token with FromEndpoint, use FromEndpoint(const SPXSTRING&), + /// and then call SetAuthorizationToken() on the created SpeechConfig instance. + /// + /// The service endpoint to connect to. + /// The subscription key. + /// A shared pointer to the new speech config instance. + static std::shared_ptr FromEndpoint(const SPXSTRING& endpoint, const SPXSTRING& subscription) + { + SPXSPEECHCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(speech_config_from_endpoint(&hconfig, Utils::ToUTF8(endpoint).c_str(), Utils::ToUTF8(subscription).c_str())); + + auto ptr = new SpeechConfig(hconfig); + return std::shared_ptr(ptr); + } + + /// + /// Creates an instance of SpeechConfig with specified endpoint. + /// This method is intended only for users who use a non-standard service endpoint. + /// Note: The query parameters specified in the endpoint URI are not changed, even if they are set by any other APIs. + /// Whether a specific query parameter is supported or not, depends on the endpoint and scenario. + /// For example, if the recognition language is defined in URI as query parameter "language=de-DE", and also set by SetSpeechRecognitionLanguage("en-US"), + /// the language setting in URI takes precedence, and the effective language is "de-DE". + /// The example only applies when the endpoint and scenario combination supports language as a query parameter. + /// Only the parameters that are not specified in the endpoint URI can be set by other APIs. + /// Note: If the endpoint requires a subscription key for authentication, use FromEndpoint(const SPXSTRING&, const SPXSTRING&) to pass + /// the subscription key as parameter. + /// To use an authorization token with FromEndpoint, use this method to create a SpeechConfig instance, and then + /// call SetAuthorizationToken() on the created SpeechConfig instance. + /// Note: Added in version 1.5.0. + /// + /// The service endpoint URI to connect to. + /// A shared pointer to the new speech config instance. + static std::shared_ptr FromEndpoint(const SPXSTRING& endpoint) + { + SPXSPEECHCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(speech_config_from_endpoint(&hconfig, Utils::ToUTF8(endpoint).c_str(), nullptr)); + + auto ptr = new SpeechConfig(hconfig); + return std::shared_ptr(ptr); + } + + /// + /// Creates an instance of the speech config with specified host and subscription. + /// This method is intended only for users who use a non-default service host. Standard resource path will be assumed. + /// For services with a non-standard resource path or no path at all, use FromEndpoint instead. + /// Note: Query parameters are not allowed in the host URI and must be set by other APIs. + /// Note: To use an authorization token with FromHost, use FromHost(const SPXSTRING&), + /// and then call SetAuthorizationToken() on the created SpeechConfig instance. + /// Note: Added in version 1.8.0. + /// + /// The service host to connect to. Format is "protocol://host:port" where ":port" is optional. + /// The subscription key. + /// A shared pointer to the new speech config instance. + static std::shared_ptr FromHost(const SPXSTRING& host, const SPXSTRING& subscription) + { + SPXSPEECHCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(speech_config_from_host(&hconfig, Utils::ToUTF8(host).c_str(), Utils::ToUTF8(subscription).c_str())); + + auto ptr = new SpeechConfig(hconfig); + return std::shared_ptr(ptr); + } + + /// + /// Creates an instance of SpeechConfig with specified host. + /// This method is intended only for users who use a non-default service host. Standard resource path will be assumed. + /// For services with a non-standard resource path or no path at all, use FromEndpoint instead. + /// Note: Query parameters are not allowed in the host URI and must be set by other APIs. + /// Note: If the host requires a subscription key for authentication, use FromHost(const SPXSTRING&, const SPXSTRING&) to pass + /// the subscription key as parameter. + /// To use an authorization token with FromHost, use this method to create a SpeechConfig instance, and then + /// call SetAuthorizationToken() on the created SpeechConfig instance. + /// Note: Added in version 1.8.0. + /// + /// The service host URI to connect to. Format is "protocol://host:port" where ":port" is optional. + /// A shared pointer to the new speech config instance. + static std::shared_ptr FromHost(const SPXSTRING& host) + { + SPXSPEECHCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(speech_config_from_host(&hconfig, Utils::ToUTF8(host).c_str(), nullptr)); + + auto ptr = new SpeechConfig(hconfig); + return std::shared_ptr(ptr); + } + + /// + /// Set the input language to the speech recognizer. + /// + /// Specifies the name of spoken language to be recognized in BCP-47 format. + void SetSpeechRecognitionLanguage(const SPXSTRING& lang) + { + property_bag_set_string(m_propertybag, static_cast(PropertyId::SpeechServiceConnection_RecoLanguage), nullptr, Utils::ToUTF8(lang).c_str()); + } + + /// + /// Gets the input language to the speech recognition. + /// The language is specified in BCP-47 format. + /// + /// The speech recognition language. + SPXSTRING GetSpeechRecognitionLanguage() const + { + return GetProperty(PropertyId::SpeechServiceConnection_RecoLanguage); + } + + /// + /// Sets the language of the speech synthesizer. + /// Added in version 1.4.0 + /// + /// Specifies the name of language (e.g. en-US) + void SetSpeechSynthesisLanguage(const SPXSTRING& lang) + { + SPX_THROW_ON_FAIL(property_bag_set_string(m_propertybag, static_cast(PropertyId::SpeechServiceConnection_SynthLanguage), nullptr, Utils::ToUTF8(lang).c_str())); + } + + /// + /// Gets the language of the speech synthesizer. + /// Added in version 1.4.0 + /// + /// The speech synthesis language. + SPXSTRING GetSpeechSynthesisLanguage() const + { + return GetProperty(PropertyId::SpeechServiceConnection_SynthLanguage); + } + + /// + /// Set the voice of the speech synthesizer. + /// Added in version 1.4.0 + /// + /// Specifies the name of voice + void SetSpeechSynthesisVoiceName(const SPXSTRING& voiceName) + { + SPX_THROW_ON_FAIL(property_bag_set_string(m_propertybag, static_cast(PropertyId::SpeechServiceConnection_SynthVoice), nullptr, Utils::ToUTF8(voiceName).c_str())); + } + + /// + /// Gets the voice of the speech synthesizer. + /// Added in version 1.4.0 + /// + /// The speech synthesis voice name. + SPXSTRING GetSpeechSynthesisVoiceName() const + { + return GetProperty(PropertyId::SpeechServiceConnection_SynthVoice); + } + + /// + /// Sets the speech synthesis output format (e.g. Riff16Khz16BitMonoPcm). + /// Added in version 1.4.0 + /// + /// Specifies the output format ID + void SetSpeechSynthesisOutputFormat(SpeechSynthesisOutputFormat formatId) + { + SPX_THROW_ON_FAIL(speech_config_set_audio_output_format(m_hconfig, static_cast(formatId))); + } + + /// + /// Gets the speech synthesis output format. + /// Added in version 1.4.0 + /// + /// The speech synthesis output format. + SPXSTRING GetSpeechSynthesisOutputFormat() const + { + return GetProperty(PropertyId::SpeechServiceConnection_SynthOutputFormat); + } + + /// + /// Sets the endpoint ID of Custom Speech or Custom Voice. + /// + /// Endpoint ID. + void SetEndpointId(const SPXSTRING& endpointId) + { + property_bag_set_string(m_propertybag, static_cast(PropertyId::SpeechServiceConnection_EndpointId), nullptr, Utils::ToUTF8(endpointId).c_str()); + } + + /// + /// Gets the endpoint ID of Custom Speech or Custom Voice. + /// + /// Endpoint ID. + SPXSTRING GetEndpointId() const + { + return GetProperty(PropertyId::SpeechServiceConnection_EndpointId); + } + + /// + /// Sets the authorization token to connect to the service. + /// Note: The caller needs to ensure that the authorization token is valid. Before the authorization token + /// expires, the caller needs to refresh it by calling this setter with a new valid token. + /// As configuration values are copied when creating a new recognizer, the new token value will not apply to recognizers that have already been created. + /// For recognizers that have been created before, you need to set authorization token of the corresponding recognizer + /// to refresh the token. Otherwise, the recognizers will encounter errors during recognition. + /// + /// The authorization token. + void SetAuthorizationToken(const SPXSTRING& token) + { + property_bag_set_string(m_propertybag, static_cast(PropertyId::SpeechServiceAuthorization_Token), nullptr, Utils::ToUTF8(token).c_str()); + } + + /// + /// Gets the authorization token to connect to the service. + /// + /// The authorization token. + SPXSTRING GetAuthorizationToken() const + { + return GetProperty(PropertyId::SpeechServiceAuthorization_Token); + } + + /// + /// Gets the subscription key that is used to create Speech Recognizer or Intent Recognizer or Translation Recognizer or Speech Synthesizer. + /// + /// The subscription key. + SPXSTRING GetSubscriptionKey() const + { + return GetProperty(PropertyId::SpeechServiceConnection_Key); + } + + /// + /// Gets the region key that used to create Speech Recognizer or Intent Recognizer or Translation Recognizer or speech Synthesizer. + /// + /// Region. + SPXSTRING GetRegion() const + { + return GetProperty(PropertyId::SpeechServiceConnection_Region); + } + + /// + /// Gets speech recognition output format (simple or detailed). + /// Note: This output format is for speech recognition result, use to get synthesized audio output format. + /// + /// Speech recognition output format. + OutputFormat GetOutputFormat() const + { + auto result = GetProperty(PropertyId::SpeechServiceResponse_RequestDetailedResultTrueFalse); + return result == Utils::ToSPXString(TrueString) ? OutputFormat::Detailed : OutputFormat::Simple; + } + + /// + /// Sets speech recognition output format (simple or detailed). + /// Note: This output format is for speech recognition result, use to set synthesized audio output format. + /// + /// Speech recognition output format + void SetOutputFormat(OutputFormat format) + { + property_bag_set_string(m_propertybag, static_cast(PropertyId::SpeechServiceResponse_RequestDetailedResultTrueFalse), nullptr, + format == OutputFormat::Detailed ? Utils::ToUTF8(TrueString) : Utils::ToUTF8(FalseString)); + } + + /// + /// Sets profanity option. + /// Added in version 1.5.0. + /// + /// Profanity option value. + void SetProfanity(ProfanityOption profanity) + { + SPX_THROW_ON_FAIL(speech_config_set_profanity(m_hconfig, (SpeechConfig_ProfanityOption)profanity)); + } + + /// + /// Enables audio logging in service. + /// Added in version 1.5.0. + /// + /// + /// Audio and content logs are stored either in Microsoft-owned storage, or in your own storage account linked + /// to your Cognitive Services subscription (Bring Your Own Storage (BYOS) enabled Speech resource). + /// + void EnableAudioLogging() + { + property_bag_set_string(m_propertybag, static_cast(PropertyId::SpeechServiceConnection_EnableAudioLogging), nullptr, TrueString); + } + + /// + /// Includes word-level timestamps in response result. + /// Added in version 1.5.0. + /// + void RequestWordLevelTimestamps() + { + property_bag_set_string(m_propertybag, static_cast(PropertyId::SpeechServiceResponse_RequestWordLevelTimestamps), nullptr, TrueString); + } + + /// + /// Enables dictation mode. Only supported in speech continuous recognition. + /// Added in version 1.5.0. + /// + void EnableDictation() + { + property_bag_set_string(m_propertybag, static_cast(PropertyId::SpeechServiceConnection_RecoMode), nullptr, "DICTATION"); + } + + /// + /// Sets proxy configuration + /// Added in version 1.1.0 + /// + /// Note: Proxy functionality is not available on macOS. This function will have no effect on this platform. + /// + /// The host name of the proxy server, without the protocol scheme (`http://`) + /// The port number of the proxy server + /// The user name of the proxy server + /// The password of the proxy server + void SetProxy(const SPXSTRING& proxyHostName, uint32_t proxyPort, const SPXSTRING& proxyUserName = SPXSTRING(), const SPXSTRING& proxyPassword = SPXSTRING()) + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, proxyHostName.empty()); + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, proxyPort == 0); + + property_bag_set_string(m_propertybag, static_cast(PropertyId::SpeechServiceConnection_ProxyHostName), nullptr, + Utils::ToUTF8(proxyHostName).c_str()); + property_bag_set_string(m_propertybag, static_cast(PropertyId::SpeechServiceConnection_ProxyPort), nullptr, + std::to_string(proxyPort).c_str()); + if (!proxyUserName.empty()) + { + property_bag_set_string(m_propertybag, static_cast(PropertyId::SpeechServiceConnection_ProxyUserName), nullptr, + Utils::ToUTF8(proxyUserName).c_str()); + } + if (!proxyPassword.empty()) + { + property_bag_set_string(m_propertybag, static_cast(PropertyId::SpeechServiceConnection_ProxyPassword), nullptr, + Utils::ToUTF8(proxyPassword).c_str()); + } + } + + /// + /// Sets a property value by name. + /// + /// The property name. + /// The property value. + void SetProperty(const SPXSTRING& name, const SPXSTRING& value) + { + property_bag_set_string(m_propertybag, -1, Utils::ToUTF8(name).c_str(), Utils::ToUTF8(value).c_str()); + } + + /// + /// Gets a property value by name. + /// + /// The parameter name. + /// The property value. + SPXSTRING GetProperty(const SPXSTRING& name) const + { + const char* value = property_bag_get_string(m_propertybag, -1, Utils::ToUTF8(name).c_str(), ""); + return Utils::ToSPXString(Utils::CopyAndFreePropertyString(value)); + } + + /// + /// Gets a property value by ID. + /// + /// The parameter id. + /// The property value. + SPXSTRING GetProperty(PropertyId id) const + { + const char* value = property_bag_get_string(m_propertybag, static_cast(id), nullptr, ""); + return Utils::ToSPXString(Utils::CopyAndFreePropertyString(value)); + } + + /// + /// Sets a property value by ID. + /// + /// The property id. + /// The property value. + void SetProperty(PropertyId id, const SPXSTRING& value) + { + property_bag_set_string(m_propertybag, static_cast(id), nullptr, Utils::ToUTF8(value).c_str()); + } + + /// + /// Sets a property value that will be passed to service using the specified channel. + /// Added in version 1.5.0. + /// + /// The property name. + /// The property value. + /// The channel used to pass the specified property to service. + void SetServiceProperty(const SPXSTRING& name, const SPXSTRING& value, ServicePropertyChannel channel) + { + SPX_THROW_ON_FAIL(speech_config_set_service_property(m_hconfig, Utils::ToUTF8(name).c_str(), Utils::ToUTF8(value).c_str(), (SpeechConfig_ServicePropertyChannel)channel)); + } + + /// + /// Destructs the object. + /// + virtual ~SpeechConfig() + { + speech_config_release(m_hconfig); + property_bag_release(m_propertybag); + } + +protected: + + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + explicit SpeechConfig(SPXSPEECHCONFIGHANDLE hconfig) + :m_hconfig(hconfig) + { + SPX_THROW_ON_FAIL(speech_config_get_property_bag(hconfig, &m_propertybag)); + } + + /// + /// Internal member variable that holds the speech config + /// + SPXSPEECHCONFIGHANDLE m_hconfig; + + /// + /// Internal member variable that holds the properties of the speech config + /// + SPXPROPERTYBAGHANDLE m_propertybag; + + /*! \endcond */ + +private: + DISABLE_COPY_AND_MOVE(SpeechConfig); + + }; + +}}} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_recognition_eventargs.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_recognition_eventargs.h new file mode 100644 index 0000000..f9106a6 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_recognition_eventargs.h @@ -0,0 +1,169 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_speech_recognition_eventargs.h: Public API declarations for SpeechRecognitionEventArgs C++ class +// + +#pragma once +#include +#include +#include +#include +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + + +/// +/// Class for speech recognition event arguments. +/// +class SpeechRecognitionEventArgs : public RecognitionEventArgs +{ +private: + + SPXEVENTHANDLE m_hevent; + std::shared_ptr m_result; + +public: + + /// + /// Constructor. + /// + /// Event handle + explicit SpeechRecognitionEventArgs(SPXEVENTHANDLE hevent) : + RecognitionEventArgs(hevent), + m_hevent(hevent), + m_result(std::make_shared(ResultHandleFromEventHandle(hevent))), + Result(m_result) + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hevent); + }; + + /// + virtual ~SpeechRecognitionEventArgs() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hevent); + SPX_THROW_ON_FAIL(recognizer_event_handle_release(m_hevent)); + }; + +#if defined(BINDING_OBJECTIVE_C) +private: +#endif + /// + /// Speech recognition event result. + /// + std::shared_ptr Result; + +#if defined(BINDING_OBJECTIVE_C) +public: +#else +protected: +#endif + + /*! \cond PROTECTED */ + + /// + /// Speech recognition event result. + /// + std::shared_ptr GetResult() const { return m_result; } + + /*! \endcond */ + +private: + + DISABLE_DEFAULT_CTORS(SpeechRecognitionEventArgs); + + SPXRESULTHANDLE ResultHandleFromEventHandle(SPXEVENTHANDLE hevent) + { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(recognizer_recognition_event_get_result(hevent, &hresult)); + return hresult; + } +}; + + +/// +/// Class for speech recognition canceled event arguments. +/// +class SpeechRecognitionCanceledEventArgs final : public SpeechRecognitionEventArgs +{ +private: + + std::shared_ptr m_cancellation; + CancellationReason m_cancellationReason; + CancellationErrorCode m_errorCode; + +public: + + /// + /// Constructor. + /// + /// Event handle + explicit SpeechRecognitionCanceledEventArgs(SPXEVENTHANDLE hevent) : + SpeechRecognitionEventArgs(hevent), + m_cancellation(CancellationDetails::FromResult(GetResult())), + m_cancellationReason(m_cancellation->Reason), + m_errorCode(m_cancellation->ErrorCode), + Reason(m_cancellationReason), + ErrorCode(m_errorCode), + ErrorDetails(m_cancellation->ErrorDetails) + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p)", __FUNCTION__, (void*)this); + }; + + /// + virtual ~SpeechRecognitionCanceledEventArgs() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p)", __FUNCTION__, (void*)this); + }; + +#if defined(BINDING_OBJECTIVE_C) +private: +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif + /// + /// The reason the result was canceled. + /// + const CancellationReason& Reason; + + /// + /// The error code in case of an unsuccessful recognition ( is set to Error). + /// If Reason is not Error, ErrorCode is set to NoError. + /// Added in version 1.1.0. + /// + const CancellationErrorCode& ErrorCode; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + /// + /// The error message in case of an unsuccessful recognition ( is set to Error). + /// + const SPXSTRING ErrorDetails; + +#if defined(BINDING_OBJECTIVE_C) +public: +#else +private: +#endif + /// + /// CancellationDetails. + /// + std::shared_ptr GetCancellationDetails() const { return m_cancellation; } + +private: + + DISABLE_DEFAULT_CTORS(SpeechRecognitionCanceledEventArgs); +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_recognition_model.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_recognition_model.h new file mode 100644 index 0000000..7f7bb70 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_recognition_model.h @@ -0,0 +1,108 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_speech_recognition_model.h: Public API declarations for SpeechRecognitionModel C++ class +// + +#pragma once +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Speech recognition model information. +/// +class SpeechRecognitionModel +{ +private: + + /// + /// Internal member variable that holds the model handle. + /// + SPXSPEECHRECOMODELHANDLE m_hmodel; + +public: + + /// + /// Creates a new instance using the provided handle. + /// + /// Model handle. + explicit SpeechRecognitionModel(SPXSPEECHRECOMODELHANDLE hmodel) : + m_hmodel(hmodel), + Name(m_name), + Locales(m_locales), + Path(m_path), + Version(m_version) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + m_name = Utils::ToSPXString(Utils::CopyAndFreePropertyString(speech_recognition_model_get_name(m_hmodel))); + m_locales = Utils::Split(Utils::CopyAndFreePropertyString(speech_recognition_model_get_locales(m_hmodel)), '|'); + m_path = Utils::ToSPXString(Utils::CopyAndFreePropertyString(speech_recognition_model_get_path(m_hmodel))); + m_version = Utils::ToSPXString(Utils::CopyAndFreePropertyString(speech_recognition_model_get_version(m_hmodel))); + } + + /// + /// Explicit conversion operator. + /// + /// Model handle. + explicit operator SPXSPEECHRECOMODELHANDLE() { return m_hmodel; } + + /// + /// Destructor. + /// + ~SpeechRecognitionModel() + { + speech_recognition_model_handle_release(m_hmodel); + } + + /// + /// Model name. + /// + const SPXSTRING& Name; + + /// + /// Locales of the model in BCP-47 format. + /// + const std::vector& Locales; + + /// + /// Model path (only valid for offline models). + /// + const SPXSTRING& Path; + + /// + /// Model version. + /// + const SPXSTRING& Version; + +private: + + DISABLE_DEFAULT_CTORS(SpeechRecognitionModel); + + /// + /// Internal member variable that holds the model name. + /// + SPXSTRING m_name; + + /// + /// Internal member variable that holds the model locales. + /// + std::vector m_locales; + + /// + /// Internal member variable that holds the model path. + /// + SPXSTRING m_path; + + /// + /// Internal member variable that holds the model version. + /// + SPXSTRING m_version; +}; + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_recognition_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_recognition_result.h new file mode 100644 index 0000000..24f0f37 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_recognition_result.h @@ -0,0 +1,45 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_speech_recognition_result.h: Public API declarations for SpeechRecognitionResult C++ class +// + +#pragma once +#include +#include +#include +#include +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + + +/// +/// Base class for speech recognition results. +/// +class SpeechRecognitionResult : public RecognitionResult +{ +public: + + explicit SpeechRecognitionResult(SPXRESULTHANDLE hresult) : + RecognitionResult(hresult) + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p) -- resultid=%s; reason=0x%x; text=%s", __FUNCTION__, (void*)this, (void*)Handle, Utils::ToUTF8(ResultId).c_str(), Reason, Utils::ToUTF8(Text).c_str()); + } + + virtual ~SpeechRecognitionResult() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)Handle); + } + + +private: + DISABLE_DEFAULT_CTORS(SpeechRecognitionResult); +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_recognizer.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_recognizer.h new file mode 100644 index 0000000..1c4e122 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_recognizer.h @@ -0,0 +1,351 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_speech_recognizer.h: Public API declarations for SpeechRecognizer C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +class Session; + +/// +/// Class for speech recognizers. +/// +class SpeechRecognizer final : public AsyncRecognizer +{ +public: + + using BaseType = AsyncRecognizer; + + /// + /// Create a speech recognizer from a speech config + /// + /// Speech configuration. + /// A smart pointer wrapped speech recognizer pointer. + static std::shared_ptr FromConfig(std::shared_ptr speechconfig, std::nullptr_t) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_speech_recognizer_from_config( + &hreco, + HandleOrInvalid(speechconfig), + HandleOrInvalid(nullptr))); + return std::make_shared(hreco); + } + + /// + /// Create a speech recognizer from an embedded speech config. + /// Added in version 1.19.0 + /// + /// Embedded speech configuration. + /// A smart pointer wrapped speech recognizer pointer. + static std::shared_ptr FromConfig(std::shared_ptr speechConfig, std::nullptr_t) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_speech_recognizer_from_config( + &hreco, + HandleOrInvalid(speechConfig), + HandleOrInvalid(nullptr))); + return std::make_shared(hreco); + } + + /// + /// Create a speech recognizer from a hybrid speech config. + /// + /// Hybrid speech configuration. + /// A smart pointer wrapped speech recognizer pointer. + static std::shared_ptr FromConfig(std::shared_ptr speechConfig, std::nullptr_t) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_speech_recognizer_from_config( + &hreco, + HandleOrInvalid(speechConfig), + HandleOrInvalid(nullptr))); + return std::make_shared(hreco); + } + + /// + /// Create a speech recognizer from a speech config and audio config. + /// + /// Speech configuration. + /// Audio configuration. + /// A smart pointer wrapped speech recognizer pointer. + static std::shared_ptr FromConfig(std::shared_ptr speechconfig, std::shared_ptr audioInput = nullptr) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_speech_recognizer_from_config( + &hreco, + HandleOrInvalid(speechconfig), + HandleOrInvalid(audioInput))); + return std::make_shared(hreco); + } + + /// + /// Create a speech recognizer from an embedded speech config and audio config. + /// Added in version 1.19.0 + /// + /// Embedded speech configuration. + /// Audio configuration. + /// A smart pointer wrapped speech recognizer pointer. + static std::shared_ptr FromConfig(std::shared_ptr speechConfig, std::shared_ptr audioConfig = nullptr) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_speech_recognizer_from_config( + &hreco, + HandleOrInvalid(speechConfig), + HandleOrInvalid(audioConfig))); + return std::make_shared(hreco); + } + + /// + /// Create a speech recognizer from a hybrid speech config and audio config. + /// + /// Hybrid speech configuration. + /// Audio configuration. + /// A smart pointer wrapped speech recognizer pointer. + static std::shared_ptr FromConfig(std::shared_ptr speechConfig, std::shared_ptr audioConfig = nullptr) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_speech_recognizer_from_config( + &hreco, + HandleOrInvalid(speechConfig), + HandleOrInvalid(audioConfig))); + return std::make_shared(hreco); + } + + /// + /// Create a speech recognizer from a speech config, auto detection source language config and audio config + /// Added in 1.8.0 + /// + /// Speech configuration. + /// Auto detection source language config. + /// Audio configuration. + /// A smart pointer wrapped speech recognizer pointer. + static std::shared_ptr FromConfig( + std::shared_ptr speechconfig, + std::shared_ptr autoDetectSourceLangConfig, + std::shared_ptr audioInput = nullptr) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_speech_recognizer_from_auto_detect_source_lang_config( + &hreco, + HandleOrInvalid(speechconfig), + HandleOrInvalid(autoDetectSourceLangConfig), + HandleOrInvalid(audioInput))); + return std::make_shared(hreco); + } + + /// + /// Create a speech recognizer from an embedded speech config, auto detection source language config and audio config + /// Added in 1.20.0 + /// + /// Embedded speech configuration. + /// Auto detection source language config. + /// Audio configuration. + /// A smart pointer wrapped speech recognizer pointer. + static std::shared_ptr FromConfig( + std::shared_ptr speechconfig, + std::shared_ptr autoDetectSourceLangConfig, + std::shared_ptr audioInput = nullptr) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_speech_recognizer_from_auto_detect_source_lang_config( + &hreco, + HandleOrInvalid(speechconfig), + HandleOrInvalid(autoDetectSourceLangConfig), + HandleOrInvalid(audioInput))); + return std::make_shared(hreco); + } + + /// + /// Create a speech recognizer from a hybrid speech config, auto detection source language config and audio config + /// + /// Hybrid speech configuration. + /// Auto detection source language config. + /// Audio configuration. + /// A smart pointer wrapped speech recognizer pointer. + static std::shared_ptr FromConfig( + std::shared_ptr speechconfig, + std::shared_ptr autoDetectSourceLangConfig, + std::shared_ptr audioInput = nullptr) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_speech_recognizer_from_auto_detect_source_lang_config( + &hreco, + HandleOrInvalid(speechconfig), + HandleOrInvalid(autoDetectSourceLangConfig), + HandleOrInvalid(audioInput))); + return std::make_shared(hreco); + } + + /// + /// Create a speech recognizer from a speech config, source language config and audio config + /// Added in 1.8.0 + /// + /// Speech configuration. + /// Source language config. + /// Audio configuration. + /// A smart pointer wrapped speech recognizer pointer. + static std::shared_ptr FromConfig( + std::shared_ptr speechconfig, + std::shared_ptr sourceLanguageConfig, + std::shared_ptr audioInput = nullptr) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_speech_recognizer_from_source_lang_config( + &hreco, + HandleOrInvalid(speechconfig), + HandleOrInvalid(sourceLanguageConfig), + HandleOrInvalid(audioInput))); + return std::make_shared(hreco); + } + + /// + /// Create a speech recognizer from a speech config, source language and audio config + /// Added in 1.8.0 + /// + /// Speech configuration. + /// Source language. + /// Audio configuration. + /// A smart pointer wrapped speech recognizer pointer. + static std::shared_ptr FromConfig( + std::shared_ptr speechconfig, + const SPXSTRING& sourceLanguage, + std::shared_ptr audioInput = nullptr) + { + return FromConfig(speechconfig, SourceLanguageConfig::FromLanguage(sourceLanguage), audioInput); + } + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Recognizer handle. + explicit SpeechRecognizer(SPXRECOHANDLE hreco) : BaseType(hreco), Properties(m_properties) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + } + + /// + /// Destructor. + /// + ~SpeechRecognizer() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + TermRecognizer(); + } + + /// + /// Starts speech recognition, and returns after a single utterance is recognized. The end of a + /// single utterance is determined by listening for silence at the end or until a maximum of about 30 + /// seconds of audio is processed. The task returns the recognition text as result. + /// Note: Since RecognizeOnceAsync() returns only a single utterance, it is suitable only for single + /// shot recognition like command or query. + /// For long-running multi-utterance recognition, use StartContinuousRecognitionAsync() instead. + /// + /// Future containing result value (a shared pointer to SpeechRecognitionResult) + /// of the asynchronous speech recognition. + /// + std::future> RecognizeOnceAsync() override + { + return BaseType::RecognizeOnceAsyncInternal(); + } + + /// + /// Asynchronously initiates continuous speech recognition operation. + /// + /// An empty future. + std::future StartContinuousRecognitionAsync() override + { + return BaseType::StartContinuousRecognitionAsyncInternal(); + } + + /// + /// Asynchronously terminates ongoing continuous speech recognition operation. + /// + /// An empty future. + std::future StopContinuousRecognitionAsync() override + { + return BaseType::StopContinuousRecognitionAsyncInternal(); + } + + /// + /// Asynchronously initiates keyword recognition operation. + /// + /// Specifies the keyword model to be used. + /// An empty future. + std::future StartKeywordRecognitionAsync(std::shared_ptr model) override + { + return BaseType::StartKeywordRecognitionAsyncInternal(model); + } + + /// + /// Asynchronously terminates keyword recognition operation. + /// + /// An empty future. + std::future StopKeywordRecognitionAsync() override + { + return BaseType::StopKeywordRecognitionAsyncInternal(); + } + + /// + /// A collection of properties and their values defined for this . + /// + PropertyCollection& Properties; + + /// + /// Gets the endpoint ID of a customized speech model that is used for speech recognition. + /// + /// the endpoint ID of a customized speech model that is used for speech recognition + SPXSTRING GetEndpointId() + { + return Properties.GetProperty(PropertyId::SpeechServiceConnection_EndpointId, SPXSTRING()); + } + + /// + /// Sets the authorization token that will be used for connecting to the service. + /// Note: The caller needs to ensure that the authorization token is valid. Before the authorization token + /// expires, the caller needs to refresh it by calling this setter with a new valid token. + /// Otherwise, the recognizer will encounter errors during recognition. + /// + /// The authorization token. + void SetAuthorizationToken(const SPXSTRING& token) + { + Properties.SetProperty(PropertyId::SpeechServiceAuthorization_Token, token); + } + + /// + /// Gets the authorization token. + /// + /// Authorization token + SPXSTRING GetAuthorizationToken() + { + return Properties.GetProperty(PropertyId::SpeechServiceAuthorization_Token, SPXSTRING()); + } + +private: + DISABLE_DEFAULT_CTORS(SpeechRecognizer); + friend class Microsoft::CognitiveServices::Speech::Session; +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_bookmark_eventargs.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_bookmark_eventargs.h new file mode 100644 index 0000000..46535e8 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_bookmark_eventargs.h @@ -0,0 +1,81 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Class for speech synthesis bookmark event arguments. +/// Added in version 1.16.0 +/// +class SpeechSynthesisBookmarkEventArgs : public EventArgs +{ +private: + + SPXEVENTHANDLE m_hEvent; + +public: + + /// + /// Constructor. + /// + /// Event handle + explicit SpeechSynthesisBookmarkEventArgs(SPXEVENTHANDLE hevent) : + m_hEvent(hevent), + ResultId(m_resultId), + Text(m_text) + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hEvent); + synthesizer_bookmark_event_get_values(hevent, &m_audioOffset); + AudioOffset = m_audioOffset; + + m_text = Utils::ToSPXString(Utils::CopyAndFreePropertyString(synthesizer_event_get_text(hevent))); + + const size_t maxCharCount = 256; + char sz[maxCharCount + 1]; + SPX_THROW_ON_FAIL(synthesizer_event_get_result_id(hevent, sz, maxCharCount)); + m_resultId = Utils::ToSPXString(sz); + }; + + /// + virtual ~SpeechSynthesisBookmarkEventArgs() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hEvent); + SPX_THROW_ON_FAIL(synthesizer_event_handle_release(m_hEvent)); + } + + /// + /// Unique result id. + /// Added in version 1.25.0 + /// + const SPXSTRING& ResultId; + + /// + /// Audio offset, in ticks (100 nanoseconds). + /// + uint64_t AudioOffset; + + /// + /// The bookmark text. + /// + const SPXSTRING& Text; + +private: + + DISABLE_DEFAULT_CTORS(SpeechSynthesisBookmarkEventArgs); + + SPXSTRING m_resultId; + uint64_t m_audioOffset{ 0 }; + SPXSTRING m_text; +}; + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_eventargs.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_eventargs.h new file mode 100644 index 0000000..b6d8321 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_eventargs.h @@ -0,0 +1,70 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_speech_synthesis_eventargs.h: Public API declarations for SpeechSynthesisEventArgs C++ class +// + +#pragma once +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + + +/// +/// Class for speech synthesis event arguments. +/// Added in version 1.4.0 +/// +class SpeechSynthesisEventArgs : public EventArgs +{ +private: + SPXEVENTHANDLE m_hevent; + std::shared_ptr m_result; + +public: + + /// + /// Constructor. + /// + /// Event handle + explicit SpeechSynthesisEventArgs(SPXEVENTHANDLE hevent) : + m_hevent(hevent), + m_result(std::make_shared(ResultHandleFromEventHandle(hevent))), + Result(m_result) + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hevent); + }; + + /// + virtual ~SpeechSynthesisEventArgs() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hevent); + SPX_THROW_ON_FAIL(synthesizer_event_handle_release(m_hevent)); + } + + /// + /// Speech synthesis event result. + /// + std::shared_ptr Result; + + +private: + + DISABLE_DEFAULT_CTORS(SpeechSynthesisEventArgs); + + SPXRESULTHANDLE ResultHandleFromEventHandle(SPXEVENTHANDLE hevent) + { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(synthesizer_synthesis_event_get_result(hevent, &hresult)); + return hresult; + } + +}; + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_request.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_request.h new file mode 100644 index 0000000..3790906 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_request.h @@ -0,0 +1,239 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_speech_config.h: Public API declarations for SpeechConfig C++ class +// +#pragma once + +#include + +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Class that defines the speech synthesis request. +/// This class is in preview and is subject to change. +/// Added in version 1.37.0 +/// +class SpeechSynthesisRequest +{ +public: + + /// + /// Represents an input stream for speech synthesis request. + /// Note: This class is in preview and may be subject to change in future versions. + /// Added in version 1.37.0 + /// + class InputStream + { + public: + friend class SpeechSynthesisRequest; + /// + /// Send a piece of text to the speech synthesis service to be synthesized. + /// + /// The text piece to be synthesized. + void Write(const SPXSTRING &text) + { + m_parent.SendTextPiece(text); + } + + /// + /// Finish the text input. + /// + void Close() + { + m_parent.FinishInput(); + } + + private: + InputStream(SpeechSynthesisRequest& parent) + : m_parent(parent) + { + } + SpeechSynthesisRequest& m_parent; + DISABLE_COPY_AND_MOVE(InputStream); + }; + + /// + /// Internal operator used to get underlying handle value. + /// + /// A handle. + explicit operator SPXREQUESTHANDLE() const { return m_hrequest; } + + /// + /// Creates a speech synthesis request, with text streaming is enabled. + /// + /// A shared pointer to the new speech synthesis request instance. + static std::shared_ptr NewTextStreamingRequest() + { + SPXREQUESTHANDLE hrequest = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(speech_synthesis_request_create(true, false, nullptr, 0, &hrequest)); + + auto ptr = new SpeechSynthesisRequest(hrequest); + return std::shared_ptr(ptr); + } + + /// + /// Gets the input stream for the speech synthesis request. + /// + /// The input stream. + InputStream& GetInputStream() + { + return m_inputStream; + } + + /// + /// Sets the pitch of the synthesized speech. + /// + /// The pitch of the synthesized speech. + void SetPitch(const SPXSTRING& pitch) { + SetProperty(PropertyId::SpeechSynthesisRequest_Pitch, pitch); + } + + /// + /// Set the speaking rate. + /// + /// The speaking rate. + void SetRate(const SPXSTRING& rate) { + SetProperty(PropertyId::SpeechSynthesisRequest_Rate, rate); + } + + /// + /// Set the speaking volume. + /// + /// The speaking volume. + void SetVolume(const SPXSTRING& volume) { + SetProperty(PropertyId::SpeechSynthesisRequest_Volume, volume); + } + + /// + /// Destructs the object. + /// + virtual ~SpeechSynthesisRequest() + { + speech_synthesis_request_release(m_hrequest); + property_bag_release(m_propertybag); + } + +protected: + + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + explicit SpeechSynthesisRequest(SPXREQUESTHANDLE hrequest) + :m_hrequest(hrequest), + m_inputStream(*this) + { + SPX_THROW_ON_FAIL(speech_synthesis_request_get_property_bag(hrequest, &m_propertybag)); + } + + /// + /// Internal member variable that holds the speech synthesis request handle. + /// + SPXREQUESTHANDLE m_hrequest; + + /// + /// Internal member variable that holds the properties of the speech synthesis request. + /// + SPXPROPERTYBAGHANDLE m_propertybag; + + InputStream m_inputStream; + + /// + /// Send a piece of text to the speech synthesis service to be synthesized, used in text streaming mode. + /// + /// The text piece to be synthesized. + void SendTextPiece(const SPXSTRING& text) + { + auto u8text = Utils::ToUTF8(text); + SPX_THROW_ON_FAIL(speech_synthesis_request_send_text_piece(m_hrequest, u8text.c_str(), static_cast(u8text.length()))); + } + + /// + /// Finish the text input, used in text streaming mode. + /// + void FinishInput() + { + SPX_THROW_ON_FAIL(speech_synthesis_request_finish(m_hrequest)); + } + + /// + /// Sets a property value by ID. + /// + /// The property id. + /// The property value. + void SetProperty(PropertyId id, const SPXSTRING& value) + { + property_bag_set_string(m_propertybag, static_cast(id), nullptr, Utils::ToUTF8(value).c_str()); + } + + /*! \endcond */ + +private: + DISABLE_COPY_AND_MOVE(SpeechSynthesisRequest); + + + +}; + +/// +/// Class that defines the speech synthesis request for personal voice (aka.ms/azureai/personal-voice). +/// This class is in preview and is subject to change. +/// Added in version 1.39.0 +/// +class PersonalVoiceSynthesisRequest: public SpeechSynthesisRequest +{ +public: + + /// + /// Creates a personal voice speech synthesis request, with text streaming is enabled. + /// + /// The name of the personal voice to be used for synthesis. + /// The name of the model. E.g., DragonLatestNeural or PhoenixLatestNeural + /// A shared pointer to the new speech synthesis request instance. + static std::shared_ptr NewTextStreamingRequest(const std::string& personalVoiceName, const std::string& modelName) + { + SPXREQUESTHANDLE hrequest = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(speech_synthesis_request_create(true, false, nullptr, 0, &hrequest)); + + SPX_THROW_ON_FAIL(speech_synthesis_request_set_voice(hrequest, nullptr, personalVoiceName.c_str(), modelName.c_str())); + + auto ptr = new PersonalVoiceSynthesisRequest(hrequest); + return std::shared_ptr(ptr); + } + + /// + /// Destructs the object. + /// + virtual ~PersonalVoiceSynthesisRequest() + { + + } + +protected: + + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + explicit PersonalVoiceSynthesisRequest(SPXREQUESTHANDLE hrequest) + :SpeechSynthesisRequest(hrequest) + {} + + /*! \endcond */ + +private: + DISABLE_COPY_AND_MOVE(PersonalVoiceSynthesisRequest); + +}; + +}}} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_result.h new file mode 100644 index 0000000..145e6ec --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_result.h @@ -0,0 +1,310 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_speech_synthesis_result.h: Public API declarations for SpeechSynthesisResult C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Contains information about result from text-to-speech synthesis. +/// Added in version 1.4.0 +/// +class SpeechSynthesisResult +{ +private: + + /// + /// Internal member variable that holds the tts result handle. + /// + SPXRESULTHANDLE m_hresult; + + /*! \cond PRIVATE */ + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXRESULTHANDLE hresult) : + PropertyCollection( + [=]() { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + synth_result_get_property_bag(hresult, &hpropbag); + return hpropbag; + }()) + { + } + }; + + /// + /// Internal member variable that holds the properties associating to the tts result. + /// + PrivatePropertyCollection m_properties; + + /*! \endcond */ + +public: + + /// + /// Creates a new instance using the provided handle. + /// + /// Result handle. + explicit SpeechSynthesisResult(SPXRESULTHANDLE hresult) : + m_hresult(hresult), + m_properties(hresult), + ResultId(m_resultId), + Reason(m_reason), + AudioDuration(m_audioDuration), + Properties(m_properties) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + const size_t maxCharCount = 1024; + char sz[maxCharCount + 1]; + + SPX_THROW_ON_FAIL(synth_result_get_result_id(hresult, sz, maxCharCount)); + m_resultId = Utils::ToSPXString(sz); + + Result_Reason resultReason; + SPX_THROW_ON_FAIL(synth_result_get_reason(hresult, &resultReason)); + m_reason = static_cast(resultReason); + + uint32_t audioLength = 0; + uint64_t audioDuration = 0; + SPX_THROW_ON_FAIL(synth_result_get_audio_length_duration(m_hresult, &audioLength, &audioDuration)); + m_audioDuration = std::chrono::milliseconds(audioDuration); + + m_audioData = std::make_shared>(audioLength); + + if (audioLength > 0) + { + uint32_t filledSize = 0; + SPX_THROW_ON_FAIL(synth_result_get_audio_data(m_hresult, m_audioData->data(), audioLength, &filledSize)); + } + } + + /// + /// Gets the size of synthesized audio in bytes. + /// + /// Length of synthesized audio + uint32_t GetAudioLength() + { + return static_cast(m_audioData->size()); + } + + /// + /// Gets the synthesized audio. + /// + /// Synthesized audio data + std::shared_ptr> GetAudioData() + { + return m_audioData; + } + + /// + /// Explicit conversion operator. + /// + /// A handle. + explicit operator SPXRESULTHANDLE() { return m_hresult; } + + /// + /// Destructor. + /// + ~SpeechSynthesisResult() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + synthesizer_result_handle_release(m_hresult); + } + + /// + /// Unique result id. + /// + const SPXSTRING& ResultId; + + /// + /// Reason of the synthesis result. + /// + const ResultReason& Reason; + + /// + /// Time duration of the synthesized audio, only valid for completed synthsis. + /// Added in version 1.21.0 + /// + const std::chrono::milliseconds& AudioDuration; + + /// + /// Collection of additional SpeechSynthesisResult properties. + /// + const PropertyCollection& Properties; + +private: + + DISABLE_DEFAULT_CTORS(SpeechSynthesisResult); + + /// + /// Internal member variable that holds the result ID. + /// + SPXSTRING m_resultId; + + /// + /// Internal member variable that holds the result reason. + /// + ResultReason m_reason; + + /// + /// Internal member variable that holds the audio data + /// + std::shared_ptr> m_audioData; + + /// + /// Internal member variable that holds the audio duration + // + std::chrono::milliseconds m_audioDuration; +}; + + +/// +/// Contains detailed information about why a result was canceled. +/// Added in version 1.4.0 +/// +class SpeechSynthesisCancellationDetails +{ +private: + + CancellationReason m_reason; + CancellationErrorCode m_errorCode; + +public: + + /// + /// Creates an instance of SpeechSynthesisCancellationDetails object for the canceled SpeechSynthesisResult. + /// + /// The result that was canceled. + /// A shared pointer to CancellationDetails. + static std::shared_ptr FromResult(std::shared_ptr result) + { + auto ptr = new SpeechSynthesisCancellationDetails(result.get()); + auto cancellation = std::shared_ptr(ptr); + return cancellation; + } + + /// + /// Creates an instance of SpeechSynthesisCancellationDetails object for the canceled SpeechSynthesisResult. + /// + /// The audio data stream that was canceled. + /// A shared pointer to CancellationDetails. + static std::shared_ptr FromStream(std::shared_ptr stream) + { + auto ptr = new SpeechSynthesisCancellationDetails(stream.get()); + auto cancellation = std::shared_ptr(ptr); + return cancellation; + } + + /// + /// The reason the result was canceled. + /// + const CancellationReason& Reason; + + /// + /// The error code in case of an unsuccessful speech synthesis ( is set to Error). + /// If Reason is not Error, ErrorCode is set to NoError. + /// + const CancellationErrorCode& ErrorCode; + + /// + /// The error message in case of an unsuccessful speech synthesis ( is set to Error). + /// + const SPXSTRING ErrorDetails; + +private: + + DISABLE_DEFAULT_CTORS(SpeechSynthesisCancellationDetails); + + SpeechSynthesisCancellationDetails(SpeechSynthesisResult* result) : + m_reason(GetCancellationReason(result)), + m_errorCode(GetCancellationErrorCode(result)), + Reason(m_reason), + ErrorCode(m_errorCode), + ErrorDetails(result->Properties.GetProperty(PropertyId::CancellationDetails_ReasonDetailedText)) + { + } + + SpeechSynthesisCancellationDetails(AudioDataStream* stream) : + m_reason(GetCancellationReason(stream)), + m_errorCode(GetCancellationErrorCode(stream)), + Reason(m_reason), + ErrorCode(m_errorCode), + ErrorDetails(stream->Properties.GetProperty(PropertyId::CancellationDetails_ReasonDetailedText)) + { + } + + Speech::CancellationReason GetCancellationReason(SpeechSynthesisResult* result) + { + Result_CancellationReason reason = CancellationReason_Error; + + SPXRESULTHANDLE hresult = (SPXRESULTHANDLE)(*result); + SPX_IFFAILED_THROW_HR(synth_result_get_reason_canceled(hresult, &reason)); + + return static_cast(reason); + } + + Speech::CancellationErrorCode GetCancellationErrorCode(SpeechSynthesisResult* result) + { + Result_CancellationErrorCode errorCode = CancellationErrorCode_NoError; + + SPXRESULTHANDLE hresult = (SPXRESULTHANDLE)(*result); + SPX_IFFAILED_THROW_HR(synth_result_get_canceled_error_code(hresult, &errorCode)); + + return static_cast(errorCode); + } + + Speech::CancellationReason GetCancellationReason(AudioDataStream* stream) + { + Result_CancellationReason reason = CancellationReason_Error; + + SPXAUDIOSTREAMHANDLE hstream = (SPXAUDIOSTREAMHANDLE)(*stream); + SPX_IFFAILED_THROW_HR(audio_data_stream_get_reason_canceled(hstream, &reason)); + + return static_cast(reason); + } + + Speech::CancellationErrorCode GetCancellationErrorCode(AudioDataStream* stream) + { + Result_CancellationErrorCode errorCode = CancellationErrorCode_NoError; + + SPXAUDIOSTREAMHANDLE hstream = (SPXAUDIOSTREAMHANDLE)(*stream); + SPX_IFFAILED_THROW_HR(audio_data_stream_get_canceled_error_code(hstream, &errorCode)); + + return static_cast(errorCode); + } +}; + +inline std::shared_ptr AudioDataStream::FromResult(std::shared_ptr result) +{ + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + if (result != nullptr) + { + hresult = (SPXRESULTHANDLE)(*result.get()); + } + + SPXAUDIOSTREAMHANDLE hstream = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(audio_data_stream_create_from_result(&hstream, hresult)); + + auto stream = new AudioDataStream(hstream); + return std::shared_ptr(stream); +} + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_viseme_eventargs.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_viseme_eventargs.h new file mode 100644 index 0000000..726b95f --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_viseme_eventargs.h @@ -0,0 +1,88 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Class for speech synthesis viseme event arguments. +/// Added in version 1.16.0 +/// +class SpeechSynthesisVisemeEventArgs : public EventArgs +{ +private: + + SPXEVENTHANDLE m_hEvent; + +public: + + /// + /// Constructor. + /// + /// Event handle + explicit SpeechSynthesisVisemeEventArgs(SPXEVENTHANDLE hevent) : + m_hEvent(hevent), + ResultId(m_resultId), + Animation(m_animation) + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hEvent); + synthesizer_viseme_event_get_values(hevent, &m_audioOffset, &m_visemeId); + AudioOffset = m_audioOffset; + VisemeId = m_visemeId; + + m_animation = Utils::ToSPXString(Utils::CopyAndFreePropertyString(synthesizer_viseme_event_get_animation(hevent))); + + const size_t maxCharCount = 256; + char sz[maxCharCount + 1]; + SPX_THROW_ON_FAIL(synthesizer_event_get_result_id(hevent, sz, maxCharCount)); + m_resultId = Utils::ToSPXString(sz); + }; + + /// + virtual ~SpeechSynthesisVisemeEventArgs() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hEvent); + SPX_THROW_ON_FAIL(synthesizer_event_handle_release(m_hEvent)); + } + + /// + /// Unique result id. + /// Added in version 1.25.0 + /// + const SPXSTRING& ResultId; + + /// + /// Audio offset, in ticks (100 nanoseconds). + /// + uint64_t AudioOffset; + + /// + /// Viseme ID. + /// + uint32_t VisemeId; + + /// + /// Animation, could be svg or other format. + /// + const SPXSTRING& Animation; + +private: + + DISABLE_DEFAULT_CTORS(SpeechSynthesisVisemeEventArgs); + + SPXSTRING m_resultId; + uint64_t m_audioOffset{ 0 }; + uint32_t m_visemeId { 0 }; + SPXSTRING m_animation; +}; + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_word_boundary_eventargs.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_word_boundary_eventargs.h new file mode 100644 index 0000000..c44fe60 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesis_word_boundary_eventargs.h @@ -0,0 +1,120 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_speech_synthesis_word_boundary_eventargs.h: Public API declarations for SpeechSynthesisWordBoundaryEventArgs C++ class +// + +#pragma once +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + + +/// +/// Class for speech synthesis word boundary event arguments. +/// Added in version 1.7.0 +/// +class SpeechSynthesisWordBoundaryEventArgs : public EventArgs +{ +private: + + SPXEVENTHANDLE m_hEvent; + +public: + + /// + /// Constructor. + /// + /// Event handle + explicit SpeechSynthesisWordBoundaryEventArgs(SPXEVENTHANDLE hevent) : + m_hEvent(hevent), + ResultId(m_resultId), + Duration(m_duration), + Text(m_text), + BoundaryType(m_boundaryType) + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hEvent); + uint64_t durationTicks; + SpeechSynthesis_BoundaryType boundaryType = SpeechSynthesis_BoundaryType_Word; + synthesizer_word_boundary_event_get_values(hevent, &m_audioOffset, &durationTicks, &m_textOffset, &m_wordLength, &boundaryType); + m_duration = std::chrono::milliseconds(durationTicks / static_cast(10000)); + m_boundaryType = static_cast(boundaryType); + AudioOffset = m_audioOffset; + TextOffset = m_textOffset; + WordLength = m_wordLength; + m_text = Utils::ToSPXString(Utils::CopyAndFreePropertyString(synthesizer_event_get_text(hevent))); + + const size_t maxCharCount = 256; + char sz[maxCharCount + 1]; + SPX_THROW_ON_FAIL(synthesizer_event_get_result_id(hevent, sz, maxCharCount)); + m_resultId = Utils::ToSPXString(sz); + }; + + /// + virtual ~SpeechSynthesisWordBoundaryEventArgs() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hEvent); + SPX_THROW_ON_FAIL(synthesizer_event_handle_release(m_hEvent)); + } + + /// + /// Unique result id. + /// Added in version 1.25.0 + /// + const SPXSTRING& ResultId; + + /// + /// Word boundary audio offset. + /// + uint64_t AudioOffset; + + /// + /// Time duration of the audio. + /// Added in version 1.21.0 + /// + const std::chrono::milliseconds& Duration; + + /// + /// Word boundary text offset. + /// + uint32_t TextOffset; + + /// + /// Word boundary word length. + /// + uint32_t WordLength; + + /// + /// The text. + /// Added in version 1.21.0 + /// + const SPXSTRING& Text; + + /// + /// Word boundary type. + /// Added in version 1.21.0 + /// + const SpeechSynthesisBoundaryType& BoundaryType; + +private: + + DISABLE_DEFAULT_CTORS(SpeechSynthesisWordBoundaryEventArgs); + + SPXSTRING m_resultId; + uint64_t m_audioOffset{ 0 }; + std::chrono::milliseconds m_duration{ 0 }; + uint32_t m_textOffset{ 0 }; + uint32_t m_wordLength{ 0 }; + SPXSTRING m_text; + SpeechSynthesisBoundaryType m_boundaryType{ SpeechSynthesisBoundaryType::Word }; +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesizer.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesizer.h new file mode 100644 index 0000000..c8b8ce8 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_synthesizer.h @@ -0,0 +1,793 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_speech_synthesizer.h: Public API declarations for SpeechSynthesizer C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Class for speech synthesizer. +/// Updated in version 1.14.0 +/// +class SpeechSynthesizer : public std::enable_shared_from_this +{ + friend class Connection; +private: + + /// + /// Internal member variable that holds the speech synthesizer handle. + /// + SPXSYNTHHANDLE m_hsynth; + + std::shared_ptr m_audioConfig; + + /*! \cond PRIVATE */ + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXSYNTHHANDLE hsynth) : + PropertyCollection( + [=]() { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + synthesizer_get_property_bag(hsynth, &hpropbag); + return hpropbag; + }()) + { + } + }; + + /// + /// Internal member variable that holds the properties of the speech synthesizer + /// + PrivatePropertyCollection m_properties; + + /*! \endcond */ + +public: + + /// + /// Create a speech synthesizer from a speech config. + /// + /// Speech configuration. + /// A smart pointer wrapped speech synthesizer pointer. + static std::shared_ptr FromConfig(std::shared_ptr speechconfig, std::nullptr_t) + { + SPXSYNTHHANDLE hsynth = SPXHANDLE_INVALID; + + SPX_THROW_ON_FAIL(::synthesizer_create_speech_synthesizer_from_config( + &hsynth, + Utils::HandleOrInvalid(speechconfig), + SPXHANDLE_INVALID)); + + auto ptr = new SpeechSynthesizer(hsynth); + return std::shared_ptr(ptr); + } + + /// + /// Create a speech synthesizer from an embedded speech config. + /// Added in version 1.19.0 + /// + /// Embedded speech configuration. + /// A smart pointer wrapped speech synthesizer pointer. + static std::shared_ptr FromConfig(std::shared_ptr speechconfig, std::nullptr_t) + { + SPXSYNTHHANDLE hsynth = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::synthesizer_create_speech_synthesizer_from_config( + &hsynth, + Utils::HandleOrInvalid(speechconfig), + SPXHANDLE_INVALID)); + auto ptr = new SpeechSynthesizer(hsynth); + return std::shared_ptr(ptr); + } + + /// + /// Create a speech synthesizer from a hybrid speech config. + /// + /// Hybrid speech configuration. + /// A smart pointer wrapped speech synthesizer pointer. + static std::shared_ptr FromConfig(std::shared_ptr speechconfig, std::nullptr_t) + { + SPXSYNTHHANDLE hsynth = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::synthesizer_create_speech_synthesizer_from_config( + &hsynth, + Utils::HandleOrInvalid(speechconfig), + SPXHANDLE_INVALID)); + auto ptr = new SpeechSynthesizer(hsynth); + return std::shared_ptr(ptr); + } + + /// + /// Create a speech synthesizer from a speech config and audio config. + /// + /// Speech configuration. + /// Audio configuration. + /// A smart pointer wrapped speech synthesizer pointer. + static std::shared_ptr FromConfig( + std::shared_ptr speechconfig, + std::shared_ptr audioconfig = Audio::AudioConfig::FromDefaultSpeakerOutput()) + { + SPXSYNTHHANDLE hsynth = SPXHANDLE_INVALID; + + SPX_THROW_ON_FAIL(::synthesizer_create_speech_synthesizer_from_config( + &hsynth, + Utils::HandleOrInvalid(speechconfig), + Utils::HandleOrInvalid(audioconfig))); + + auto ptr = new SpeechSynthesizer(hsynth); + auto synthesizer = std::shared_ptr(ptr); + synthesizer->m_audioConfig = audioconfig; + return synthesizer; + } + + /// + /// Create a speech synthesizer from an embedded speech config and audio config. + /// Added in version 1.19.0 + /// + /// Embedded speech configuration. + /// Audio configuration. + /// A smart pointer wrapped speech synthesizer pointer. + static std::shared_ptr FromConfig( + std::shared_ptr speechconfig, + std::shared_ptr audioconfig = Audio::AudioConfig::FromDefaultSpeakerOutput()) + { + SPXSYNTHHANDLE hsynth = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::synthesizer_create_speech_synthesizer_from_config( + &hsynth, + Utils::HandleOrInvalid(speechconfig), + Utils::HandleOrInvalid(audioconfig))); + auto ptr = new SpeechSynthesizer(hsynth); + auto synthesizer = std::shared_ptr(ptr); + synthesizer->m_audioConfig = audioconfig; + return synthesizer; + } + + /// + /// Create a speech synthesizer from a hybrid speech config and audio config. + /// + /// Hybrid speech configuration. + /// Audio configuration. + /// A smart pointer wrapped speech synthesizer pointer. + static std::shared_ptr FromConfig( + std::shared_ptr speechconfig, + std::shared_ptr audioconfig = Audio::AudioConfig::FromDefaultSpeakerOutput()) + { + SPXSYNTHHANDLE hsynth = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::synthesizer_create_speech_synthesizer_from_config( + &hsynth, + Utils::HandleOrInvalid(speechconfig), + Utils::HandleOrInvalid(audioconfig))); + auto ptr = new SpeechSynthesizer(hsynth); + auto synthesizer = std::shared_ptr(ptr); + synthesizer->m_audioConfig = audioconfig; + return synthesizer; + } + + /// + /// Create a speech synthesizer from a speech config, auto detection source language config and audio config + /// Added in 1.13.0 + /// + /// Speech configuration. + /// Auto detection source language config. + /// Audio configuration. + /// A smart pointer wrapped speech synthesizer pointer. + static std::shared_ptr FromConfig( + std::shared_ptr speechconfig, + std::shared_ptr autoDetectSourceLangConfig, + std::shared_ptr audioconfig = Audio::AudioConfig::FromDefaultSpeakerOutput()) + { + SPXSYNTHHANDLE hsynth; + + SPX_THROW_ON_FAIL(::synthesizer_create_speech_synthesizer_from_auto_detect_source_lang_config( + &hsynth, + Utils::HandleOrInvalid(speechconfig), + Utils::HandleOrInvalid(autoDetectSourceLangConfig), + Utils::HandleOrInvalid(audioconfig))); + + auto ptr = new SpeechSynthesizer(hsynth); + auto synthesizer = std::shared_ptr(ptr); + synthesizer->m_audioConfig = audioconfig; + return synthesizer; + } + + /// + /// Execute the speech synthesis on plain text, synchronously. + /// + /// The plain text for synthesis. + /// A smart pointer wrapping a speech synthesis result. + std::shared_ptr SpeakText(const std::string& text) + { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::synthesizer_speak_text(m_hsynth, text.data(), static_cast(text.length()), &hresult)); + + return std::make_shared(hresult); + } + + /// + /// Execute the speech synthesis on plain text, synchronously. + /// Added in 1.9.0 + /// + /// The plain text for synthesis. + /// A smart pointer wrapping a speech synthesis result. + std::shared_ptr SpeakText(const std::wstring& text) + { + return SpeakText(Utils::ToUTF8(text)); + } + + /// + /// Execute the speech synthesis on SSML, synchronously. + /// + /// The SSML for synthesis. + /// A smart pointer wrapping a speech synthesis result. + std::shared_ptr SpeakSsml(const std::string& ssml) + { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::synthesizer_speak_ssml(m_hsynth, ssml.data(), static_cast(ssml.length()), &hresult)); + + return std::make_shared(hresult); + } + + /// + /// Execute the speech synthesis on SSML, synchronously. + /// Added in version 1.9.0 + /// + /// The SSML for synthesis. + /// A smart pointer wrapping a speech synthesis result. + std::shared_ptr SpeakSsml(const std::wstring& ssml) + { + return SpeakSsml(Utils::ToUTF8(ssml)); + } + + /// + /// Execute the speech synthesis on request, synchronously. + /// This API could be used to synthesize speech from an input text stream, to reduce latency for text generation scenarios. + /// Note: the feature is in preview and is subject to change. + /// Added in version 1.37.0 + /// + /// The synthesis request. + /// A smart pointer wrapping a speech synthesis result. + std::shared_ptr Speak(const std::shared_ptr& request) + { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::synthesizer_speak_request(m_hsynth, Utils::HandleOrInvalid(request), &hresult)); + + return std::make_shared(hresult); + } + + /// + /// Execute the speech synthesis on plain text, asynchronously. + /// + /// The plain text for synthesis. + /// An asynchronous operation representing the synthesis. It returns a value of as result. + std::future> SpeakTextAsync(const std::string& text) + { + auto keepAlive = this->shared_from_this(); + + auto future = std::async(std::launch::async, [keepAlive, this, text]() -> std::shared_ptr { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPXASYNCHANDLE hasync = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::synthesizer_speak_text_async(m_hsynth, text.data(), static_cast(text.length()), &hasync)); + SPX_EXITFN_ON_FAIL(::synthesizer_speak_async_wait_for(hasync, UINT32_MAX, &hresult)); + + SPX_EXITFN_CLEANUP: + auto releaseHr = synthesizer_async_handle_release(hasync); + SPX_REPORT_ON_FAIL(releaseHr); + + return std::make_shared(hresult); + }); + + return future; + } + + /// + /// Execute the speech synthesis on plain text, asynchronously. + /// Added in version 1.9.0 + /// + /// The plain text for synthesis. + /// An asynchronous operation representing the synthesis. It returns a value of as result. + std::future> SpeakTextAsync(const std::wstring& text) + { + return SpeakTextAsync(Utils::ToUTF8(text)); + } + + /// + /// Execute the speech synthesis on SSML, asynchronously. + /// + /// The SSML for synthesis. + /// An asynchronous operation representing the synthesis. It returns a value of as result. + std::future> SpeakSsmlAsync(const std::string& ssml) + { + auto keepAlive = this->shared_from_this(); + + auto future = std::async(std::launch::async, [keepAlive, this, ssml]() -> std::shared_ptr { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPXASYNCHANDLE hasync = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::synthesizer_speak_ssml_async(m_hsynth, ssml.data(), static_cast(ssml.length()), &hasync)); + SPX_EXITFN_ON_FAIL(::synthesizer_speak_async_wait_for(hasync, UINT32_MAX, &hresult)); + + SPX_EXITFN_CLEANUP: + auto releaseHr = synthesizer_async_handle_release(hasync); + SPX_REPORT_ON_FAIL(releaseHr); + + return std::make_shared(hresult); + }); + + return future; + } + + /// + /// Execute the speech synthesis on SSML, asynchronously. + /// Added in version 1.9.0 + /// + /// The SSML for synthesis. + /// An asynchronous operation representing the synthesis. It returns a value of as result. + std::future> SpeakSsmlAsync(const std::wstring& ssml) + { + return SpeakSsmlAsync(Utils::ToUTF8(ssml)); + } + + /// + /// Execute the speech synthesis on on request, synchronously. + /// This API could be used to synthesize speech from an input text stream, to reduce latency for text generation scenarios. + /// Note: the feature is in preview and is subject to change. + /// Added in version 1.37.0 + /// + /// The synthesis request. + /// An asynchronous operation representing the synthesis. It returns a value of as result. + std::future> SpeakAsync(const std::shared_ptr& request) + { + auto keepAlive = this->shared_from_this(); + + auto future = std::async(std::launch::async, [keepAlive, this, request]() -> std::shared_ptr { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPXASYNCHANDLE hasync = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::synthesizer_speak_request_async(m_hsynth, Utils::HandleOrInvalid(request), &hasync)); + SPX_EXITFN_ON_FAIL(::synthesizer_speak_async_wait_for(hasync, UINT32_MAX, &hresult)); + + SPX_EXITFN_CLEANUP: + auto releaseHr = synthesizer_async_handle_release(hasync); + SPX_REPORT_ON_FAIL(releaseHr); + + return std::make_shared(hresult); + }); + + return future; + } + + /// + /// Start the speech synthesis on plain text, synchronously. + /// + /// The plain text for synthesis. + /// A smart pointer wrapping a speech synthesis result. + std::shared_ptr StartSpeakingText(const std::string& text) + { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::synthesizer_start_speaking_text(m_hsynth, text.data(), static_cast(text.length()), &hresult)); + + return std::make_shared(hresult); + } + + /// + /// Start the speech synthesis on plain text, synchronously. + /// Added in version 1.9.0 + /// + /// The plain text for synthesis. + /// A smart pointer wrapping a speech synthesis result. + std::shared_ptr StartSpeakingText(const std::wstring& text) + { + return StartSpeakingText(Utils::ToUTF8(text)); + } + + /// + /// Start the speech synthesis on SSML, synchronously. + /// + /// The SSML for synthesis. + /// A smart pointer wrapping a speech synthesis result. + std::shared_ptr StartSpeakingSsml(const std::string& ssml) + { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::synthesizer_start_speaking_ssml(m_hsynth, ssml.data(), static_cast(ssml.length()), &hresult)); + + return std::make_shared(hresult); + } + + /// + /// Start the speech synthesis on SSML, synchronously. + /// Added in version 1.9.0 + /// + /// The SSML for synthesis. + /// A smart pointer wrapping a speech synthesis result. + std::shared_ptr StartSpeakingSsml(const std::wstring& ssml) + { + return StartSpeakingSsml(Utils::ToUTF8(ssml)); + } + + /// + /// Start the speech synthesis on on request, synchronously. + /// This API could be used to synthesize speech from an input text stream, to reduce latency for text generation scenarios. + /// Note: the feature is in preview and is subject to change. + /// Added in version 1.37.0 + /// + /// The synthesis request. + /// A smart pointer wrapping a speech synthesis result. + std::shared_ptr StartSpeaking(const std::shared_ptr& request) + { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::synthesizer_start_speaking_request(m_hsynth, Utils::HandleOrInvalid(request), &hresult)); + + return std::make_shared(hresult); + } + + /// + /// Start the speech synthesis on plain text, asynchronously. + /// + /// The plain text for synthesis. + /// An asynchronous operation representing the synthesis. It returns a value of as result. + std::future> StartSpeakingTextAsync(const std::string& text) + { + auto keepAlive = this->shared_from_this(); + + auto future = std::async(std::launch::async, [keepAlive, this, text]() -> std::shared_ptr { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPXASYNCHANDLE hasync = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::synthesizer_start_speaking_text_async(m_hsynth, text.data(), static_cast(text.length()), &hasync)); + SPX_EXITFN_ON_FAIL(::synthesizer_speak_async_wait_for(hasync, UINT32_MAX, &hresult)); + + SPX_EXITFN_CLEANUP: + auto releaseHr = synthesizer_async_handle_release(hasync); + SPX_REPORT_ON_FAIL(releaseHr); + + return std::make_shared(hresult); + }); + + return future; + } + + /// + /// Start the speech synthesis on plain text, asynchronously. + /// Added in version 1.9.0 + /// + /// The plain text for synthesis. + /// An asynchronous operation representing the synthesis. It returns a value of as result. + std::future> StartSpeakingTextAsync(const std::wstring& text) + { + return StartSpeakingTextAsync(Utils::ToUTF8(text)); + } + + /// + /// Start the speech synthesis on SSML, asynchronously. + /// + /// The SSML for synthesis. + /// An asynchronous operation representing the synthesis. It returns a value of as result. + std::future> StartSpeakingSsmlAsync(const std::string& ssml) + { + auto keepAlive = this->shared_from_this(); + + auto future = std::async(std::launch::async, [keepAlive, this, ssml]() -> std::shared_ptr { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPXASYNCHANDLE hasync = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::synthesizer_start_speaking_ssml_async(m_hsynth, ssml.data(), static_cast(ssml.length()), &hasync)); + SPX_EXITFN_ON_FAIL(::synthesizer_speak_async_wait_for(hasync, UINT32_MAX, &hresult)); + + SPX_EXITFN_CLEANUP: + auto releaseHr = synthesizer_async_handle_release(hasync); + SPX_REPORT_ON_FAIL(releaseHr); + + return std::make_shared(hresult); + }); + + return future; + } + + /// + /// Start the speech synthesis on SSML, asynchronously. + /// Added in version 1.9.0 + /// + /// The SSML for synthesis. + /// An asynchronous operation representing the synthesis. It returns a value of as result. + std::future> StartSpeakingSsmlAsync(const std::wstring& ssml) + { + return StartSpeakingSsmlAsync(Utils::ToUTF8(ssml)); + } + + /// + /// Stop the speech synthesis, asynchronously. + /// Added in version 1.14.0 + /// + /// An empty future. + std::future StopSpeakingAsync() + { + auto keepAlive = this->shared_from_this(); + + auto future = std::async(std::launch::async, [keepAlive, this]() -> void { + SPXASYNCHANDLE hasyncStop = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::synthesizer_stop_speaking_async(m_hsynth, &hasyncStop)); + SPX_EXITFN_ON_FAIL(::synthesizer_stop_speaking_async_wait_for(hasyncStop, UINT32_MAX)); + + SPX_EXITFN_CLEANUP: + auto releaseHr = synthesizer_async_handle_release(hasyncStop); + SPX_REPORT_ON_FAIL(releaseHr); + }); + + return future; + } + + /// + /// Get the available voices, asynchronously. + /// Added in version 1.16.0 + /// + /// Specify the locale of voices, in BCP-47 format; or leave it empty to get all available voices. + /// An asynchronous operation representing the voices list. It returns a value of as result. + std::future> GetVoicesAsync(const SPXSTRING& locale = SPXSTRING()) + { + const auto keepAlive = this->shared_from_this(); + + auto future = std::async(std::launch::async, [keepAlive, locale, this]() -> std::shared_ptr { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPXASYNCHANDLE hasync = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::synthesizer_get_voices_list_async(m_hsynth, Utils::ToUTF8(locale).c_str(), &hasync)); + SPX_EXITFN_ON_FAIL(::synthesizer_get_voices_list_async_wait_for(hasync, UINT32_MAX, &hresult)); + + SPX_EXITFN_CLEANUP: + auto releaseHr = synthesizer_async_handle_release(hasync); + SPX_REPORT_ON_FAIL(releaseHr); + + return std::make_shared(hresult); + }); + + return future; + } + + /// + /// Sets the authorization token that will be used for connecting to the service. + /// Note: The caller needs to ensure that the authorization token is valid. Before the authorization token + /// expires, the caller needs to refresh it by calling this setter with a new valid token. + /// Otherwise, the synthesizer will encounter errors while speech synthesis. + /// Added in version 1.7.0 + /// + /// The authorization token. + void SetAuthorizationToken(const SPXSTRING& token) + { + Properties.SetProperty(PropertyId::SpeechServiceAuthorization_Token, token); + } + + /// + /// Gets the authorization token. + /// Added in version 1.7.0 + /// + /// Authorization token + SPXSTRING GetAuthorizationToken() const + { + return Properties.GetProperty(PropertyId::SpeechServiceAuthorization_Token, SPXSTRING()); + } + + /// + /// Destructor. + /// + ~SpeechSynthesizer() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + // Disconnect the event signals in reverse construction order + BookmarkReached.DisconnectAll(); + VisemeReceived.DisconnectAll(); + WordBoundary.DisconnectAll(); + SynthesisCanceled.DisconnectAll(); + SynthesisCompleted.DisconnectAll(); + Synthesizing.DisconnectAll(); + SynthesisStarted.DisconnectAll(); + + synthesizer_handle_release(m_hsynth); + } + + /// + /// A collection of properties and their values defined for this . + /// + PropertyCollection& Properties; + + /// + /// The event signals that a speech synthesis result is received when the synthesis just started. + /// + EventSignal SynthesisStarted; + + /// + /// The event signals that a speech synthesis result is received while the synthesis is on going. + /// + EventSignal Synthesizing; + + /// + /// The event signals that a speech synthesis result is received when the synthesis completed. + /// + EventSignal SynthesisCompleted; + + /// + /// The event signals that a speech synthesis result is received when the synthesis is canceled. + /// + EventSignal SynthesisCanceled; + + /// + /// The event signals that a speech synthesis word boundary is received while the synthesis is on going. + /// Added in version 1.7.0 + /// + EventSignal WordBoundary; + + /// + /// The event signals that a speech synthesis viseme event is received while the synthesis is on going. + /// Added in version 1.16.0 + /// + EventSignal VisemeReceived; + + /// + /// The event signals that a speech synthesis bookmark is reached while the synthesis is on going. + /// Added in version 1.16.0 + /// + EventSignal BookmarkReached; + +private: + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Synthesizer handle. + explicit SpeechSynthesizer(SPXSYNTHHANDLE hsynth) : + m_hsynth(hsynth), + m_properties(hsynth), + Properties(m_properties), + SynthesisStarted(GetSpeechSynthesisEventConnectionsChangedCallback()), + Synthesizing(GetSpeechSynthesisEventConnectionsChangedCallback()), + SynthesisCompleted(GetSpeechSynthesisEventConnectionsChangedCallback()), + SynthesisCanceled(GetSpeechSynthesisEventConnectionsChangedCallback()), + WordBoundary(GetWordBoundaryEventConnectionsChangedCallback()), + VisemeReceived(GetVisemeEventConnectionsChangedCallback()), + BookmarkReached(GetBookmarkEventConnectionsChangedCallback()) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + } + + std::function&)> GetSpeechSynthesisEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& eventSignal) { + if (&eventSignal == &SynthesisStarted) + { + synthesizer_started_set_callback(m_hsynth, SynthesisStarted.IsConnected() ? FireEvent_SynthesisStarted : nullptr, this); + } + else if (&eventSignal == &Synthesizing) + { + synthesizer_synthesizing_set_callback(m_hsynth, Synthesizing.IsConnected() ? FireEvent_Synthesizing : nullptr, this); + } + else if (&eventSignal == &SynthesisCompleted) + { + synthesizer_completed_set_callback(m_hsynth, SynthesisCompleted.IsConnected() ? FireEvent_SynthesisCompleted : nullptr, this); + } + else if (&eventSignal == &SynthesisCanceled) + { + synthesizer_canceled_set_callback(m_hsynth, SynthesisCanceled.IsConnected() ? FireEvent_SynthesisCanceled : nullptr, this); + } + }; + } + + std::function&)> GetWordBoundaryEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& eventSignal) { + if (&eventSignal == &WordBoundary) + { + synthesizer_word_boundary_set_callback(m_hsynth, WordBoundary.IsConnected() ? FireEvent_WordBoundary : nullptr, this); + } + }; + } + + std::function&)> GetVisemeEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& eventSignal) { + if (&eventSignal == &VisemeReceived) + { + synthesizer_viseme_received_set_callback(m_hsynth, VisemeReceived.IsConnected() ? FireEvent_VisemeReceived : nullptr, this); + } + }; + } + + std::function&)> GetBookmarkEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& eventSignal) { + if (&eventSignal == &BookmarkReached) + { + synthesizer_bookmark_reached_set_callback(m_hsynth, BookmarkReached.IsConnected() ? FireEvent_BookmarkReached : nullptr, this); + } + }; + } + + static void FireEvent_SynthesisStarted(SPXSYNTHHANDLE hsynth, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hsynth); + std::unique_ptr synthEvent{ new SpeechSynthesisEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->SynthesisStarted.Signal(*synthEvent.get()); + } + + static void FireEvent_Synthesizing(SPXSYNTHHANDLE hsynth, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hsynth); + std::unique_ptr synthEvent{ new SpeechSynthesisEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->Synthesizing.Signal(*synthEvent.get()); + } + + static void FireEvent_SynthesisCompleted(SPXSYNTHHANDLE hsynth, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hsynth); + std::unique_ptr synthEvent{ new SpeechSynthesisEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->SynthesisCompleted.Signal(*synthEvent.get()); + } + + static void FireEvent_SynthesisCanceled(SPXSYNTHHANDLE hsynth, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hsynth); + std::unique_ptr synthEvent{ new SpeechSynthesisEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->SynthesisCanceled.Signal(*synthEvent.get()); + } + + static void FireEvent_WordBoundary(SPXSYNTHHANDLE hsynth, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hsynth); + std::unique_ptr wordBoundaryEvent{ new SpeechSynthesisWordBoundaryEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->WordBoundary.Signal(*wordBoundaryEvent.get()); + } + + static void FireEvent_VisemeReceived(SPXSYNTHHANDLE hsynth, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hsynth); + std::unique_ptr visemeReceivedEvent{ new SpeechSynthesisVisemeEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->VisemeReceived.Signal(*visemeReceivedEvent.get()); + } + + static void FireEvent_BookmarkReached(SPXSYNTHHANDLE hsynth, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hsynth); + std::unique_ptr bookmarkReachedEvent{ new SpeechSynthesisBookmarkEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->BookmarkReached.Signal(*bookmarkReachedEvent.get()); + } +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_translation_config.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_translation_config.h new file mode 100644 index 0000000..1b7d785 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_translation_config.h @@ -0,0 +1,213 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once + +#include +#include + +#include "speechapi_c_common.h" +#include "speechapi_c_speech_config.h" +#include "speechapi_c_speech_translation_config.h" +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Translation { + +/// +/// Class that defines configurations for translation with speech input. +/// +class SpeechTranslationConfig final : public SpeechConfig +{ +public: + /// + /// Creates an instance of the speech translation config with specified subscription key and region. + /// + /// The subscription key. + /// The region name (see the region page). + /// Shared pointer to the speech translation config instance. + static std::shared_ptr FromSubscription(const SPXSTRING& subscription, const SPXSTRING& region) + { + SPXSPEECHCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(speech_translation_config_from_subscription(&hconfig, Utils::ToUTF8(subscription).c_str(), Utils::ToUTF8(region).c_str())); + return std::shared_ptr(new SpeechTranslationConfig(hconfig)); + } + + /// + /// Creates an instance of the speech translation config with specified authorization token and region. + /// + /// The authorization token. + /// The region name (see the region page). + /// Shared pointer to the speech translation config instance. + static std::shared_ptr FromAuthorizationToken(const SPXSTRING& authToken, const SPXSTRING& region) + { + SPXSPEECHCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(speech_translation_config_from_authorization_token(&hconfig, Utils::ToUTF8(authToken).c_str(), Utils::ToUTF8(region).c_str())); + return std::shared_ptr(new SpeechTranslationConfig(hconfig)); + } + + // + /// Creates an instance of the speech translation config with specified endpoint and subscription. + /// This method is intended only for users who use a non-standard service endpoint. + /// Note: The query parameters specified in the endpoint URI are not changed, even if they are set by any other APIs. + /// For example, if the recognition language is defined in URI as query parameter "language=de-DE", and also set by SetSpeechRecognitionLanguage("en-US"), + /// the language setting in URI takes precedence, and the effective language is "de-DE". + /// Only the parameters that are not specified in the endpoint URI can be set by other APIs. + /// Note: To use an authorization token with FromEndpoint, please use FromEndpoint(const SPXSTRING&), + /// and then call SetAuthorizationToken() on the created SpeechTranslationConfig instance. + /// + /// The service endpoint to connect to. + /// The subscription key. + /// Shared pointer to the new SpeechTranslationConfig instance. + static std::shared_ptr FromEndpoint(const SPXSTRING& endpoint, const SPXSTRING& subscription) + { + SPXSPEECHCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(speech_translation_config_from_endpoint(&hconfig, Utils::ToUTF8(endpoint).c_str(), Utils::ToUTF8(subscription).c_str())); + return std::shared_ptr(new SpeechTranslationConfig(hconfig)); + } + + /// + /// Creates an instance of the speech translation config with specified endpoint. + /// This method is intended only for users who use a non-standard service endpoint. + /// Note: The query parameters specified in the endpoint URI are not changed, even if they are set by any other APIs. + /// For example, if the recognition language is defined in URI as query parameter "language=de-DE", and also set by SetSpeechRecognitionLanguage("en-US"), + /// the language setting in URI takes precedence, and the effective language is "de-DE". + /// Only the parameters that are not specified in the endpoint URI can be set by other APIs. + /// Note: if the endpoint requires a subscription key for authentication, please use FromEndpoint(const SPXSTRING&, const SPXSTRING&) to pass + /// the subscription key as parameter. + /// To use an authorization token with FromEndpoint, use this method to create a SpeechTranslationConfig instance, and then + /// call SetAuthorizationToken() on the created SpeechTranslationConfig instance. + /// Note: Added in version 1.5.0. + /// + /// The service endpoint to connect to. + /// A shared pointer to the new SpeechTranslationConfig instance. + static std::shared_ptr FromEndpoint(const SPXSTRING& endpoint) + { + SPXSPEECHCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(speech_translation_config_from_endpoint(&hconfig, Utils::ToUTF8(endpoint).c_str(), nullptr)); + return std::shared_ptr(new SpeechTranslationConfig(hconfig)); + } + + /// + /// Creates an instance of the speech translation config with specified host and subscription. + /// This method is intended only for users who use a non-default service host. Standard resource path will be assumed. + /// For services with a non-standard resource path or no path at all, use FromEndpoint instead. + /// Note: Query parameters are not allowed in the host URI and must be set by other APIs. + /// Note: To use an authorization token with FromHost, use FromHost(const SPXSTRING&), + /// and then call SetAuthorizationToken() on the created SpeechTranslationConfig instance. + /// Note: Added in version 1.8.0. + /// + /// The service host to connect to. Format is "protocol://host:port" where ":port" is optional. + /// The subscription key. + /// Shared pointer to the new SpeechTranslationConfig instance. + static std::shared_ptr FromHost(const SPXSTRING& host, const SPXSTRING& subscription) + { + SPXSPEECHCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(speech_translation_config_from_host(&hconfig, Utils::ToUTF8(host).c_str(), Utils::ToUTF8(subscription).c_str())); + return std::shared_ptr(new SpeechTranslationConfig(hconfig)); + } + + /// + /// Creates an instance of the speech translation config with specified host. + /// This method is intended only for users who use a non-default service host. Standard resource path will be assumed. + /// For services with a non-standard resource path or no path at all, use FromEndpoint instead. + /// Note: Query parameters are not allowed in the host URI and must be set by other APIs. + /// Note: If the host requires a subscription key for authentication, use FromHost(const SPXSTRING&, const SPXSTRING&) to pass + /// the subscription key as parameter. + /// To use an authorization token with FromHost, use this method to create a SpeechTranslationConfig instance, and then + /// call SetAuthorizationToken() on the created SpeechTranslationConfig instance. + /// Note: Added in version 1.8.0. + /// + /// The service host to connect to. Format is "protocol://host:port" where ":port" is optional. + /// A shared pointer to the new SpeechTranslationConfig instance. + static std::shared_ptr FromHost(const SPXSTRING& host) + { + SPXSPEECHCONFIGHANDLE hconfig = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(speech_translation_config_from_host(&hconfig, Utils::ToUTF8(host).c_str(), nullptr)); + return std::shared_ptr(new SpeechTranslationConfig(hconfig)); + } + + /// + /// Adds a target language for translation. + /// + /// Translation target language to add. + void AddTargetLanguage(const SPXSTRING& language) + { + SPX_THROW_ON_FAIL(speech_translation_config_add_target_language(m_hconfig, Utils::ToUTF8(language).c_str())); + } + + /// + /// Removes a target language for translation. + /// Added in release 1.7.0. + /// + /// Translation target language to remove. + void RemoveTargetLanguage(const SPXSTRING& language) + { + SPX_THROW_ON_FAIL(speech_translation_config_remove_target_language(m_hconfig, Utils::ToUTF8(language).c_str())); + } + + /// + /// Sets a Category Id that will be passed to service. Category Id is used to find the custom model. + /// + /// Category Id to set. + void SetCustomModelCategoryId(const SPXSTRING& categoryId) + { + SPX_THROW_ON_FAIL(speech_translation_config_set_custom_model_category_id(m_hconfig, Utils::ToUTF8(categoryId).c_str())); + } + + /// + /// Gets target languages for translation. + /// + /// Vector of translation target languages. + std::vector GetTargetLanguages() const + { + std::vector result; + auto targetLanguages = Utils::ToUTF8(GetProperty(PropertyId::SpeechServiceConnection_TranslationToLanguages)); + if (targetLanguages.empty()) + return result; + + // Getting languages one by one. + std::stringstream languageStream(targetLanguages); + std::string token; + while (std::getline(languageStream, token, CommaDelim)) + { + result.push_back(Utils::ToSPXString(token)); + } + return result; + } + + /// + /// Sets output voice name. + /// + /// Voice name to set. + void SetVoiceName(const SPXSTRING& voice) + { + property_bag_set_string(m_propertybag, static_cast(PropertyId::SpeechServiceConnection_TranslationVoice), nullptr, Utils::ToUTF8(voice).c_str()); + } + + /// + /// Gets output voice name. + /// + /// Output voice name. + SPXSTRING GetVoiceName() const + { + return GetProperty(PropertyId::SpeechServiceConnection_TranslationVoice); + } + +private: + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + explicit SpeechTranslationConfig(SPXSPEECHCONFIGHANDLE hconfig) : SpeechConfig(hconfig) { } + + DISABLE_COPY_AND_MOVE(SpeechTranslationConfig); + +}; + +}}}} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_translation_model.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_translation_model.h new file mode 100644 index 0000000..b94513d --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_speech_translation_model.h @@ -0,0 +1,120 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_speech_translation_model.h: Public API declarations for SpeechTranslationModel C++ class +// + +#pragma once +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Speech translation model information. +/// +class SpeechTranslationModel +{ +private: + + /// + /// Internal member variable that holds the model handle. + /// + SPXSPEECHRECOMODELHANDLE m_hmodel; + +public: + + /// + /// Creates a new instance using the provided handle. + /// + /// Model handle. + explicit SpeechTranslationModel(SPXSPEECHRECOMODELHANDLE hmodel) : + m_hmodel(hmodel), + Name(m_name), + SourceLanguages(m_sourceLanguages), + TargetLanguages(m_targetLanguages), + Path(m_path), + Version(m_version) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + m_name = Utils::ToSPXString(Utils::CopyAndFreePropertyString(speech_translation_model_get_name(m_hmodel))); + m_sourceLanguages = Utils::Split(Utils::CopyAndFreePropertyString(speech_translation_model_get_source_languages(m_hmodel)), '|'); + m_targetLanguages = Utils::Split(Utils::CopyAndFreePropertyString(speech_translation_model_get_target_languages(m_hmodel)), '|'); + m_path = Utils::ToSPXString(Utils::CopyAndFreePropertyString(speech_translation_model_get_path(m_hmodel))); + m_version = Utils::ToSPXString(Utils::CopyAndFreePropertyString(speech_translation_model_get_version(m_hmodel))); + } + + /// + /// Explicit conversion operator. + /// + /// Model handle. + explicit operator SPXSPEECHRECOMODELHANDLE() { return m_hmodel; } + + /// + /// Destructor. + /// + ~SpeechTranslationModel() + { + speech_translation_model_handle_release(m_hmodel); + } + + /// + /// Model name. + /// + const SPXSTRING& Name; + + /// + /// Source languages that the model supports. + /// + const std::vector& SourceLanguages; + + /// + /// Target languages that the model supports. + /// + const std::vector& TargetLanguages; + + /// + /// Model path (only valid for offline models). + /// + const SPXSTRING& Path; + + /// + /// Model version. + /// + const SPXSTRING& Version; + +private: + + DISABLE_DEFAULT_CTORS(SpeechTranslationModel); + + /// + /// Internal member variable that holds the model name. + /// + SPXSTRING m_name; + + /// + /// Internal member variable that holds the model source languages. + /// + std::vector m_sourceLanguages; + + /// + /// Internal member variable that holds the model target languages. + /// + std::vector m_targetLanguages; + + /// + /// Internal member variable that holds the model path. + /// + SPXSTRING m_path; + + /// + /// Internal member variable that holds the model version. + /// + SPXSTRING m_version; +}; + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_string_helpers.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_string_helpers.h new file mode 100644 index 0000000..4dcbb0e --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_string_helpers.h @@ -0,0 +1,137 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define SPXSTRING std::string +#define SPXSTRING_EMPTY std::string() + +namespace Microsoft{ +namespace CognitiveServices { +namespace Speech { +namespace Utils { + +namespace Details { + + inline std::string to_string(const std::wstring& value) + { + const auto size = pal_wstring_to_string(nullptr, value.c_str(), 0); + auto buffer = std::make_unique(size); + pal_wstring_to_string(buffer.get(), value.c_str(), size); + return std::string{ buffer.get() }; + } + + inline std::wstring to_string(const std::string& value) + { + const auto size = pal_string_to_wstring(nullptr, value.c_str(), 0); + auto buffer = std::make_unique(size); + pal_string_to_wstring(buffer.get(), value.c_str(), size); + return std::wstring{ buffer.get() }; + } +} + +inline std::string ToSPXString(const char* value) +{ + return value == nullptr ? "" : value; +} + +inline std::string ToSPXString(const std::string& value) +{ + return value; +} + +inline std::string ToUTF8(const std::wstring& value) +{ + return Details::to_string(value); +} + +inline std::string ToUTF8(const wchar_t* value) +{ + if (!value) + return ""; + return ToUTF8(std::wstring(value)); +} + +inline std::string ToUTF8(const std::string& value) +{ + return value; +} + +inline const char* ToUTF8(const char* value) +{ + return value; +} + +inline static std::string CopyAndFreePropertyString(const char* value) +{ + std::string copy = (value == nullptr) ? "" : value; + property_bag_free_string(value); + return copy; +} + +template +inline static size_t Find(const TCHAR* pStr, const size_t numChars, const TCHAR find, size_t startAt = 0) +{ + for (size_t i = startAt; i < numChars; i++) + { + TCHAR c = pStr[i]; + if (c == '\0') + { + break; + } + else if (c == find) + { + return i; + } + } + + return (std::numeric_limits::max)(); // weird syntax to avoid Windows min/max macros +} + +template +static std::vector> Split(const TCHAR* pStr, const size_t numChars, const TCHAR delim) +{ + std::vector> result; + if (pStr == nullptr) + { + return result; + } + + size_t start = 0; + size_t end = Find(pStr, numChars, delim, 0); + while (end != (std::numeric_limits::max)()) + { + result.push_back(std::basic_string(pStr + start, end - start)); + start = end + 1; + end = Find(pStr, numChars, delim, start); + } + + if (start < numChars) + { + result.push_back(std::basic_string(pStr + start, numChars - start)); + } + + return result; +} + +template +inline static std::vector> Split(const std::basic_string& str, const TCHAR delim) +{ + return Split(str.c_str(), str.size(), delim); +} + +}}}} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_synthesis_voices_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_synthesis_voices_result.h new file mode 100644 index 0000000..cc287c3 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_synthesis_voices_result.h @@ -0,0 +1,165 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_synthesis_voices_result.h: Public API declarations for SynthesisVoicesResult C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Contains information about result from voices list of speech synthesizers. +/// Added in version 1.16.0 +/// +class SynthesisVoicesResult +{ +private: + + /// + /// Internal member variable that holds the voices list result handle. + /// + SPXRESULTHANDLE m_hresult; + + /*! \cond PRIVATE */ + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXRESULTHANDLE hresult) : + PropertyCollection( + [=]() { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + synthesis_voices_result_get_property_bag(hresult, &hpropbag); + return hpropbag; + }()) + { + } + }; + + /// + /// Internal member variable that holds the properties associating to the voices list result. + /// + PrivatePropertyCollection m_properties; + + /*! \endcond */ + +public: + + /// + /// Creates a new instance using the provided handle. + /// + /// Result handle. + explicit SynthesisVoicesResult(SPXRESULTHANDLE hresult) : + m_hresult(hresult), + m_properties(hresult), + Voices(m_voices), + ErrorDetails(m_errorDetails), + ResultId(m_resultId), + Reason(m_reason), + Properties(m_properties) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + uint32_t voiceNum; + SPX_THROW_ON_FAIL(::synthesis_voices_result_get_voice_num(hresult, &voiceNum)); + m_voices = std::vector>(voiceNum); + + for (uint32_t i = 0; i < voiceNum; ++i) + { + SPXRESULTHANDLE hVoice = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(::synthesis_voices_result_get_voice_info(m_hresult, i, &hVoice)); + m_voices[i] = std::make_shared(hVoice); + } + + const size_t maxCharCount = 1024; + char sz[maxCharCount + 1]; + SPX_THROW_ON_FAIL(synthesis_voices_result_get_result_id(hresult, sz, maxCharCount)); + m_resultId = Utils::ToSPXString(sz); + + Result_Reason resultReason = ResultReason_NoMatch; + SPX_THROW_ON_FAIL(synthesis_voices_result_get_reason(hresult, &resultReason)); + m_reason = static_cast(resultReason); + + m_errorDetails = m_properties.GetProperty(PropertyId::CancellationDetails_ReasonDetailedText); + } + + /// + /// Explicit conversion operator. + /// + /// A handle. + explicit operator SPXRESULTHANDLE() { return m_hresult; } + + /// + /// Destructor. + /// + ~SynthesisVoicesResult() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + synthesizer_result_handle_release(m_hresult); + } + + /// + /// Retrieved voices. + /// + const std::vector>& Voices; + + /// + /// Error details. + /// + const SPXSTRING& ErrorDetails; + + /// + /// Unique result id. + /// + const SPXSTRING& ResultId; + + /// + /// Reason of the voices list result. + /// + const ResultReason& Reason; + + /// + /// Collection of additional SynthesisVoicesResult properties. + /// + const PropertyCollection& Properties; + +private: + + DISABLE_DEFAULT_CTORS(SynthesisVoicesResult); + + /// + /// Internal member variable that holds the result ID. + /// + SPXSTRING m_resultId; + + /// + /// Internal member variable that holds the result reason. + /// + ResultReason m_reason; + + /// + /// Internal member variable that holds the voices list. + /// + std::vector> m_voices; + + /// + /// Internal member variable that holds the error details. + /// + SPXSTRING m_errorDetails; +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_translation_eventargs.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_translation_eventargs.h new file mode 100644 index 0000000..42f54ef --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_translation_eventargs.h @@ -0,0 +1,235 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// + +#pragma once +#include +#include +#include +#include +#include + + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Translation { + + +/// +/// Defines payload that is sent with the event or . +/// +class TranslationRecognitionEventArgs : public RecognitionEventArgs +{ +private: + + SPXEVENTHANDLE m_hevent; + std::shared_ptr m_result; + +public: + + /// + /// It is intended for internal use only. It creates an instance of . + /// + /// The handle returned by recognizer in C-API. + explicit TranslationRecognitionEventArgs(SPXEVENTHANDLE hevent) : + RecognitionEventArgs(hevent), + m_hevent(hevent), + m_result(std::make_shared(ResultHandleFromEventHandle(hevent))), + Result(m_result) + { + UNUSED(m_hevent); + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hevent); + }; + + /// + /// Destructs the instance. + /// + virtual ~TranslationRecognitionEventArgs() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hevent); + recognizer_event_handle_release(m_hevent); + }; + +#if defined(BINDING_OBJECTIVE_C) +private: +#endif + /// + /// Contains the translation recognition result. + /// + std::shared_ptr Result; + +#if defined(BINDING_OBJECTIVE_C) +public: +#else +protected: +#endif + + /*! \cond PROTECTED */ + + /// + /// Contains the translation text result. + /// + std::shared_ptr GetResult() const { return m_result; } + + /*! \endcond */ + +private: + DISABLE_DEFAULT_CTORS(TranslationRecognitionEventArgs); + + SPXRESULTHANDLE ResultHandleFromEventHandle(SPXEVENTHANDLE hevent) + { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(recognizer_recognition_event_get_result(hevent, &hresult)); + return hresult; + } +}; + + +/// +/// Class for translation recognition canceled event arguments. +/// +class TranslationRecognitionCanceledEventArgs final : public TranslationRecognitionEventArgs +{ +private: + + std::shared_ptr m_cancellation; + CancellationReason m_cancellationReason; + CancellationErrorCode m_errorCode; + +public: + + /// + /// Constructor. + /// + /// Event handle + explicit TranslationRecognitionCanceledEventArgs(SPXEVENTHANDLE hevent) : + TranslationRecognitionEventArgs(hevent), + m_cancellation(CancellationDetails::FromResult(GetResult())), + m_cancellationReason(m_cancellation->Reason), + m_errorCode(m_cancellation->ErrorCode), + Reason(m_cancellationReason), + ErrorCode(m_errorCode), + ErrorDetails(m_cancellation->ErrorDetails) + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p)", __FUNCTION__, (void*)this); + }; + + /// + virtual ~TranslationRecognitionCanceledEventArgs() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p)", __FUNCTION__, (void*)this); + }; + +#if defined(BINDING_OBJECTIVE_C) +private: +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif + /// + /// The reason the result was canceled. + /// + const CancellationReason& Reason; + + /// + /// The error code in case of an unsuccessful recognition ( is set to Error). + /// If Reason is not Error, ErrorCode is set to NoError. + /// Added in version 1.1.0. + /// + const CancellationErrorCode& ErrorCode; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + /// + /// The error message in case of an unsuccessful recognition ( is set to Error). + /// + const SPXSTRING ErrorDetails; + +#if defined(BINDING_OBJECTIVE_C) +public: +#else +private: +#endif + /// + /// CancellationDetails. + /// + std::shared_ptr GetCancellationDetails() const { return m_cancellation; } + +private: + + DISABLE_DEFAULT_CTORS(TranslationRecognitionCanceledEventArgs); +}; + + + +/// +/// Defines payload that is sent with the event . +/// +class TranslationSynthesisEventArgs final : public SessionEventArgs +{ +private: + + SPXEVENTHANDLE m_hevent; + std::shared_ptr m_result; + +public: + /// + /// It is intended for internal use only. It creates an instance of . + /// + /// The handle returned by recognizer in C-API. + explicit TranslationSynthesisEventArgs(SPXEVENTHANDLE hevent) : + SessionEventArgs(hevent), + m_hevent(hevent), + m_result(std::make_shared(SynthesisResultHandleFromEventHandle(hevent))), + Result(m_result) + { + UNUSED(m_hevent); + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hevent); + }; + + /// + /// Destructs the instance. + /// + virtual ~TranslationSynthesisEventArgs() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)m_hevent); + recognizer_event_handle_release(m_hevent); + }; + +#if defined(BINDING_OBJECTIVE_C) +private: +#endif + /// + /// Contains the translation synthesis result. + /// + std::shared_ptr Result; + +#if defined(BINDING_OBJECTIVE_C) +public: +#else +private: +#endif + /// + /// Contains the translation synthesis result. + /// + std::shared_ptr GetResult() const { return m_result; } + +private: + + DISABLE_DEFAULT_CTORS(TranslationSynthesisEventArgs); + + SPXRESULTHANDLE SynthesisResultHandleFromEventHandle(SPXEVENTHANDLE hevent) + { + SPXRESULTHANDLE hresult = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(recognizer_recognition_event_get_result(hevent, &hresult)); + return hresult; + } +}; + +} } } } // Microsoft::CognitiveServices::Speech::Translation diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_translation_recognizer.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_translation_recognizer.h new file mode 100644 index 0000000..867487d --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_translation_recognizer.h @@ -0,0 +1,352 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_translation_recognizer.h: Public API declarations for translation recognizer in C++. +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Translation { + +/// +/// Performs translation on the speech input. +/// +class TranslationRecognizer final : public AsyncRecognizer +{ +public: + /// + /// Create a translation recognizer from a speech config + /// + /// Speech configuration. + /// A smart pointer wrapped speech recognizer pointer. + static std::shared_ptr FromConfig(std::shared_ptr speechconfig, std::nullptr_t) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_translation_recognizer_from_config( + &hreco, + HandleOrInvalid(speechconfig), + HandleOrInvalid(nullptr))); + return std::make_shared(hreco); + } + + /// + /// Create a translation recognizer from an embedded speech config + /// + /// Embedded speech configuration. + /// A smart pointer wrapped translation recognizer pointer. + static std::shared_ptr FromConfig(std::shared_ptr speechConfig, std::nullptr_t) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_translation_recognizer_from_config( + &hreco, + HandleOrInvalid(speechConfig), + HandleOrInvalid(nullptr))); + return std::make_shared(hreco); + } + + /// + /// Create a translation recognizer from a hybrid speech config + /// + /// Hybrid speech configuration. + /// A smart pointer wrapped translation recognizer pointer. + static std::shared_ptr FromConfig(std::shared_ptr speechConfig, std::nullptr_t) + { + SPXRECOHANDLE hreco; + SPX_THROW_ON_FAIL(::recognizer_create_translation_recognizer_from_config( + &hreco, + HandleOrInvalid(speechConfig), + HandleOrInvalid(nullptr))); + return std::make_shared(hreco); + } + + /// + /// Create a translation recognizer from a translation config and an audio config. + /// Users should use this function to create a translation recognizer. + /// + /// Speech translation config. + /// Audio config. + /// The shared smart pointer of the created translation recognizer. + static std::shared_ptr FromConfig(std::shared_ptr speechconfig, std::shared_ptr audioInput = nullptr) + { + SPXRECOHANDLE hreco { SPXHANDLE_INVALID }; + SPX_THROW_ON_FAIL(::recognizer_create_translation_recognizer_from_config( + &hreco, + HandleOrInvalid(speechconfig), + HandleOrInvalid(audioInput))); + return std::make_shared(hreco); + } + + /// + /// Create a translation recognizer from an embedded speech config and audio config. + /// + /// Embedded speech config. + /// Audio config. + /// A smart pointer wrapped translation recognizer pointer. + static std::shared_ptr FromConfig(std::shared_ptr speechConfig, std::shared_ptr audioConfig = nullptr) + { + SPXRECOHANDLE hreco{ SPXHANDLE_INVALID }; + SPX_THROW_ON_FAIL(::recognizer_create_translation_recognizer_from_config( + &hreco, + HandleOrInvalid(speechConfig), + HandleOrInvalid(audioConfig))); + return std::make_shared(hreco); + } + + /// + /// Create a translation recognizer from a hybrid speech config and audio config. + /// + /// Hybrid speech config. + /// Audio config. + /// A smart pointer wrapped translation recognizer pointer. + static std::shared_ptr FromConfig(std::shared_ptr speechConfig, std::shared_ptr audioConfig = nullptr) + { + SPXRECOHANDLE hreco{ SPXHANDLE_INVALID }; + SPX_THROW_ON_FAIL(::recognizer_create_translation_recognizer_from_config( + &hreco, + HandleOrInvalid(speechConfig), + HandleOrInvalid(audioConfig))); + return std::make_shared(hreco); + } + + /// + /// Create a translation recognizer from a translation config, auto detection source language config and an audio config. + /// Users should use this function to create a translation recognizer. + /// + /// Speech translation config. + /// Auto detection source language config. + /// Audio config. + /// The shared smart pointer of the created translation recognizer. + static std::shared_ptr FromConfig( + std::shared_ptr speechconfig, + std::shared_ptr autoDetectSourceLangConfig, + std::shared_ptr audioInput = nullptr) + { + SPXRECOHANDLE hreco { SPXHANDLE_INVALID }; + SPX_THROW_ON_FAIL(::recognizer_create_translation_recognizer_from_auto_detect_source_lang_config( + &hreco, + HandleOrInvalid(speechconfig), + HandleOrInvalid(autoDetectSourceLangConfig), + HandleOrInvalid(audioInput))); + return std::make_shared(hreco); + } + + /// + /// Create a translation recognizer from an embedded speech config, auto detection source language config and audio config. + /// + /// Embedded speech config. + /// Auto detection source language config. + /// Audio config. + /// The shared smart pointer of the created translation recognizer. + static std::shared_ptr FromConfig( + std::shared_ptr speechConfig, + std::shared_ptr autoDetectSourceLangConfig, + std::shared_ptr audioInput = nullptr) + { + SPXRECOHANDLE hreco{ SPXHANDLE_INVALID }; + SPX_THROW_ON_FAIL(::recognizer_create_translation_recognizer_from_auto_detect_source_lang_config( + &hreco, + HandleOrInvalid(speechConfig), + HandleOrInvalid(autoDetectSourceLangConfig), + HandleOrInvalid(audioInput))); + return std::make_shared(hreco); + } + + // The AsyncRecognizer only deals with events for translation text result. The audio output event + // is managed by OnTranslationSynthesisResult. + using BaseType = AsyncRecognizer; + + /// + /// It is intended for internal use only. It creates an instance of . + /// + /// + /// It is recommended to use SpeechTranslationConfig to create an instance of . This method is mainly + /// used in case where a recognizer handle has been created by methods via C-API. + /// + /// The handle of the recognizer that is returned by C-API. + explicit TranslationRecognizer(SPXRECOHANDLE hreco) : + BaseType(hreco), + Properties(m_properties), + Synthesizing(GetTranslationAudioEventConnectionsChangedCallback()) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + } + + /// + /// Deconstruct the instance. + /// + ~TranslationRecognizer() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + Synthesizing.DisconnectAll(); + TermRecognizer(); + } + + /// + /// Starts translation recognition, and returns after a single utterance is recognized. The end of a + /// single utterance is determined by listening for silence at the end or until a maximum of about 30 + /// seconds of audio is processed. The task returns the recognized text as well as the translation. + /// Note: Since RecognizeOnceAsync() returns only a single utterance, it is suitable only for single + /// shot recognition like command or query. + /// For long-running multi-utterance recognition, use StartContinuousRecognitionAsync() instead. + /// + /// An asynchronous operation representing the recognition. It returns a value of as result. + std::future> RecognizeOnceAsync() override + { + return BaseType::RecognizeOnceAsyncInternal(); + } + + /// + /// Starts translation on a continous audio stream, until StopContinuousRecognitionAsync() is called. + /// User must subscribe to events to receive recognition results. + /// + /// An asynchronous operation that starts the translation. + std::future StartContinuousRecognitionAsync() override + { + return BaseType::StartContinuousRecognitionAsyncInternal(); + } + + /// + /// Stops continuous translation. + /// + /// A task representing the asynchronous operation that stops the translation. + std::future StopContinuousRecognitionAsync() override { return BaseType::StopContinuousRecognitionAsyncInternal(); } + + /// + /// Starts keyword recognition on a continuous audio stream, until StopKeywordRecognitionAsync() is called. + /// + /// Specifies the keyword model to be used. + /// An asynchronous operation that starts the keyword recognition. + std::future StartKeywordRecognitionAsync(std::shared_ptr model) override + { + return BaseType::StartKeywordRecognitionAsyncInternal(model); + }; + + /// + /// Stops continuous keyword recognition. + /// + /// A task representing the asynchronous operation that stops the keyword recognition. + std::future StopKeywordRecognitionAsync() override + { + return BaseType::StopKeywordRecognitionAsyncInternal(); + }; + + /// + /// Sets the authorization token that will be used for connecting to the service. + /// Note: The caller needs to ensure that the authorization token is valid. Before the authorization token + /// expires, the caller needs to refresh it by calling this setter with a new valid token. + /// Otherwise, the recognizer will encounter errors during recognition. + /// + /// A string that represents the endpoint id. + void SetAuthorizationToken(const SPXSTRING& token) + { + Properties.SetProperty(PropertyId::SpeechServiceAuthorization_Token, token); + } + + /// + /// Gets the authorization token. + /// + /// Authorization token + SPXSTRING GetAuthorizationToken() + { + return Properties.GetProperty(PropertyId::SpeechServiceAuthorization_Token, SPXSTRING()); + } + + /// + /// Adds a target language for translation. + /// Added in version 1.7.0. + /// + /// Translation target language to add. + void AddTargetLanguage(const SPXSTRING& language) + { + SPX_THROW_HR_IF(SPXERR_INVALID_HANDLE, m_hreco == SPXHANDLE_INVALID); + SPX_THROW_ON_FAIL(::translator_add_target_language(m_hreco, Utils::ToUTF8(language).c_str())); + } + + /// + /// Removes a target language for translation. + /// Added in version 1.7.0. + /// + /// Translation target language to remove. + void RemoveTargetLanguage(const SPXSTRING& language) + { + SPX_THROW_HR_IF(SPXERR_INVALID_HANDLE, m_hreco == SPXHANDLE_INVALID); + SPX_THROW_ON_FAIL(::translator_remove_target_language(m_hreco, Utils::ToUTF8(language).c_str())); + } + + /// + /// Gets target languages for translation. + /// Added in version 1.7.0. + /// + /// Vector of translation target languages. + std::vector GetTargetLanguages() const + { + std::vector result; + auto targetLanguages = Utils::ToUTF8(Properties.GetProperty(PropertyId::SpeechServiceConnection_TranslationToLanguages)); + if (targetLanguages.empty()) + return result; + + // Getting languages one by one. + std::stringstream languageStream(targetLanguages); + std::string token; + while (std::getline(languageStream, token, CommaDelim)) + { + result.push_back(Utils::ToSPXString(token)); + } + return result; + } + + /// + /// The collection of properties and their values defined for this . + /// + PropertyCollection& Properties; + + /// + /// The event signals that a translation synthesis result is received. + /// + EventSignal Synthesizing; + +private: + + DISABLE_DEFAULT_CTORS(TranslationRecognizer); + + friend class Microsoft::CognitiveServices::Speech::Session; + + std::function&)> GetTranslationAudioEventConnectionsChangedCallback() + { + return [=, this](const EventSignal& audioEvent) { + if (&audioEvent == &Synthesizing) + { + translator_synthesizing_audio_set_callback(m_hreco, Synthesizing.IsConnected() ? FireEvent_TranslationSynthesisResult : nullptr, this); + } + }; + } + + static void FireEvent_TranslationSynthesisResult(SPXRECOHANDLE hreco, SPXEVENTHANDLE hevent, void* pvContext) + { + UNUSED(hreco); + std::unique_ptr recoEvent{ new TranslationSynthesisEventArgs(hevent) }; + + auto pThis = static_cast(pvContext); + auto keepAlive = pThis->shared_from_this(); + pThis->Synthesizing.Signal(*recoEvent.get()); + } +}; +} } } } // Microsoft::CognitiveServices::Speech::Translation diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_translation_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_translation_result.h new file mode 100644 index 0000000..bbabcd6 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_translation_result.h @@ -0,0 +1,175 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_translation_result.h: Public API declarations for TranslationResult C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Translation { + +/// +/// Defines the translation text result. +/// +class TranslationRecognitionResult : public RecognitionResult +{ +private: + + std::map m_translations; + +public: + /// + /// It is intended for internal use only. It creates an instance of . + /// + /// The handle of the result returned by recognizer in C-API. + explicit TranslationRecognitionResult(SPXRESULTHANDLE resultHandle) : + RecognitionResult(resultHandle), + Translations(m_translations) + { + PopulateResultFields(resultHandle); + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p) -- resultid=%s.", __FUNCTION__, (void*)this, (void*)Handle, ResultId.c_str()); + }; + + /// + /// Destructs the instance. + /// + virtual ~TranslationRecognitionResult() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p)", __FUNCTION__, (void*)this, (void*)Handle); + } + + /// + /// Presents the translation results. Each item in the map is a key value pair, where key is the language tag of the translated text, + /// and value is the translation text in that language. + /// + const std::map& Translations; + +private: + void PopulateResultFields(SPXRESULTHANDLE resultHandle) + { + SPX_INIT_HR(hr); + + size_t count = 0; + hr = translation_text_result_get_translation_count(resultHandle, &count); + SPX_THROW_ON_FAIL(hr); + + size_t maxLanguageSize = 0; + size_t maxTextSize = 0; + + for (size_t i = 0; i < count; i++) + { + size_t languageSize = 0; + size_t textSize = 0; + + hr = translation_text_result_get_translation(resultHandle, i, nullptr, nullptr, &languageSize, &textSize); + SPX_THROW_ON_FAIL(hr); + + maxLanguageSize = (std::max)(maxLanguageSize, languageSize); + maxTextSize = (std::max)(maxTextSize, textSize); + } + + auto targetLanguage = std::make_unique(maxLanguageSize); + auto translationText = std::make_unique(maxTextSize); + for (size_t i = 0; i < count; i++) + { + hr = translation_text_result_get_translation(resultHandle, i, targetLanguage.get(), translationText.get(), &maxLanguageSize, &maxTextSize); + SPX_THROW_ON_FAIL(hr); + m_translations[Utils::ToSPXString(targetLanguage.get())] = Utils::ToSPXString(translationText.get()); + } + + SPX_DBG_TRACE_VERBOSE("Translation phrases: numberentries: %d", (int)m_translations.size()); +#ifdef _DEBUG + for (const auto& cf : m_translations) + { + (void)(cf); // prevent warning for cf when compiling release builds + SPX_DBG_TRACE_VERBOSE(" phrase for %s: %s", cf.first.c_str(), cf.second.c_str()); + } +#endif + }; + + DISABLE_DEFAULT_CTORS(TranslationRecognitionResult); +}; + + +/// +/// Defines the translation synthesis result, i.e. the voice output of the translated text in the target language. +/// +class TranslationSynthesisResult +{ +private: + + ResultReason m_reason; + std::vector m_audioData; + +public: + /// + /// It is intended for internal use only. It creates an instance of + /// + /// The handle of the result returned by recognizer in C-API. + explicit TranslationSynthesisResult(SPXRESULTHANDLE resultHandle) : + Reason(m_reason), + Audio(m_audioData) + { + PopulateResultFields(resultHandle); + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p, handle=0x%p) reason=0x%x", __FUNCTION__, (void*)this, (void*)resultHandle, Reason); + }; + + /// + /// Destructs the instance. + /// + virtual ~TranslationSynthesisResult() + { + SPX_DBG_TRACE_VERBOSE("%s (this=0x%p)", __FUNCTION__, (void*)this); + }; + + /// + /// Recognition reason. + /// + const ResultReason& Reason; + + /// + /// The voice output of the translated text in the target language. + /// + const std::vector& Audio; + + +private: + + DISABLE_DEFAULT_CTORS(TranslationSynthesisResult); + + void PopulateResultFields(SPXRESULTHANDLE resultHandle) + { + SPX_INIT_HR(hr); + + Result_Reason resultReason = ResultReason_NoMatch; + SPX_THROW_ON_FAIL(hr = result_get_reason(resultHandle, &resultReason)); + m_reason = (ResultReason)resultReason; + + size_t bufLen = 0; + hr = translation_synthesis_result_get_audio_data(resultHandle, nullptr, &bufLen); + if (hr == SPXERR_BUFFER_TOO_SMALL) + { + m_audioData.resize(bufLen); + hr = translation_synthesis_result_get_audio_data(resultHandle, m_audioData.data(), &bufLen); + } + SPX_THROW_ON_FAIL(hr); + + SPX_DBG_TRACE_VERBOSE("Translation synthesis: audio length: %zu, vector size: %zu", bufLen, m_audioData.size()); + }; +}; + + +} } } } // Microsoft::CognitiveServices::Speech::Translation diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_user.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_user.h new file mode 100644 index 0000000..0f0a597 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_user.h @@ -0,0 +1,77 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_user.h: Public API declarations for User C++ class +// + +#pragma once + +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Transcription { + +constexpr size_t MAX_USER_ID_LEN = 1024; + +/// +/// Represents a user in a conversation. +/// Added in version 1.5.0. +/// +class User +{ +public: + + /// + /// Create a user with identification string. + /// + /// A user id. + /// A user object + static std::shared_ptr FromUserId(const SPXSTRING& userId) + { + SPXUSERHANDLE m_huser = SPXHANDLE_INVALID; + SPX_THROW_ON_FAIL(user_create_from_id(Utils::ToUTF8(userId).c_str(), &m_huser)); + return std::make_shared(m_huser); + } + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// A user handle. + explicit User(SPXUSERHANDLE huser = SPXHANDLE_INVALID) : m_huser(huser) { } + + /// + /// Virtual destructor. + /// + virtual ~User() { user_release_handle(m_huser); } + + /// + /// Internal. Explicit conversion operator. + /// + /// A handle. + explicit operator SPXUSERHANDLE() const { return m_huser; } + + /// + /// Get user's id. + /// + /// user's id. + SPXSTRING GetId() const + { + char user_id[MAX_USER_ID_LEN+1]; + std::memset(user_id, 0, MAX_USER_ID_LEN+1); + SPX_THROW_ON_FAIL(user_get_id(m_huser, user_id, MAX_USER_ID_LEN)); + + return user_id; + } + +private: + + DISABLE_COPY_AND_MOVE(User); + + SPXUSERHANDLE m_huser; + +}; + +}}}} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_utils.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_utils.h new file mode 100644 index 0000000..21b4a98 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_utils.h @@ -0,0 +1,312 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_utils.h: General utility classes and functions. +// + +#pragma once + +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Utils { + +/// +/// Base class that disables the copy constructor +/// +struct NonCopyable +{ + /// + /// Default destructor. + /// + NonCopyable() = default; + + /// + /// Virtual destructor. + /// + virtual ~NonCopyable() = default; + + /// + /// Disable copy constructor. + /// + NonCopyable(const NonCopyable&) = delete; + + /// + /// Disable copy assignment operator. + /// + /// Reference to the object. + NonCopyable& operator=(const NonCopyable &) = delete; +}; + +/// +/// Base class that disables the move constructor +/// +struct NonMovable +{ + /// + /// Default destructor. + /// + NonMovable() = default; + + /// + /// Virtual destructor. + /// + virtual ~NonMovable() = default; + + /// + /// Disable move constructor. + /// + NonMovable(NonMovable &&) = delete; + + /// + /// Disable move assignment operator. + /// + /// Reference to the object. + NonMovable& operator=(NonMovable &&) = delete; +}; + +template +SPXHANDLE CallFactoryMethodRight(F method, Args&&... args) +{ + SPXHANDLE handle; + auto hr = method(std::forward(args)..., &handle); + SPX_THROW_ON_FAIL(hr); + return handle; +} + +template +SPXHANDLE CallFactoryMethodLeft(F method, Args&&... args) +{ + SPXHANDLE handle; + auto hr = method(&handle, std::forward(args)...); + SPX_THROW_ON_FAIL(hr); + return handle; +} + +/// +/// Helper class implementing the scope guard idiom. +/// (The given function will be executed on destruction) +/// +template +class ScopeGuard +{ +public: + ScopeGuard(ScopeGuard&&) = default; + ScopeGuard(const ScopeGuard&) = delete; + + explicit ScopeGuard(F f): m_fn{ f } + {} + + ~ScopeGuard() + { + m_fn(); + } + +private: + F m_fn; +}; + +/// +/// Creates a scope guard with the given function. +/// +template +ScopeGuard MakeScopeGuard(F fn) +{ + return ScopeGuard{ fn }; +} + +/// +/// A wrapper around ABI handles that simplifies resource cleanup on exit +/// +/// The type of the ABI handle +/// The default value to set the handle to when initialising or after destroying +/// The return type of the free function +/// The signature of the free function called to release the ABI handle +template< + typename THandle, + typename TRet = AZACHR, + typename TFreeFunc = TRet(AZAC_API_CALLTYPE*)(THandle)> +class AbiHandleWrapper : public NonCopyable +{ +private: + THandle m_handle; + TFreeFunc m_free; + bool m_isValid; + +public: + /// + /// The signature of the free function + /// + using FreeFunc = TFreeFunc; + + /// + /// Creates and ABI handle wrapper for SPXHANDLE types initializing the handle + /// to be SPXHANDLE_INVALID + /// + /// The function used to release the ABI handle + template< + typename IsHandle = THandle, + std::enable_if_t::value, bool> = true + > + AbiHandleWrapper(TFreeFunc freeFunc) : + m_handle{ SPXHANDLE_INVALID }, + m_free{ freeFunc }, + m_isValid{ false } + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, freeFunc == nullptr); + } + + /// + /// Creates an ABI handle wrapper + /// + /// The function used to release the ABI handle + template< + typename IsHandle = THandle, + std::enable_if_t::value, bool> = true + > + AbiHandleWrapper(TFreeFunc freeFunc) : + m_handle{ nullptr }, + m_free{ freeFunc }, + m_isValid{ false } + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, freeFunc == nullptr); + } + + /// + /// Creates an ABI handle wrapper + /// + /// The function used to release the ABI handle + /// The initial ABI handle value + AbiHandleWrapper(TFreeFunc freeFunc, THandle&& handle) : + m_handle{ std::move(handle) }, + m_free{ freeFunc }, + m_isValid{ true } + { + SPX_THROW_HR_IF(SPXERR_INVALID_ARG, freeFunc == nullptr); + } + + /// + /// Destructor + /// + ~AbiHandleWrapper() { Destroy(); } + + /// + /// Move constructor + /// + /// The other item being moved + AbiHandleWrapper(AbiHandleWrapper&& other) : + m_handle{ other.m_handle }, + m_free{ other.m_free }, + m_isValid{ other.m_isValid } + { + other.m_handle = THandle{}; + other.m_free = nullptr; + other.m_isValid = false; + } + + /// + /// Move assignment operator + /// + /// The item being moved + /// Reference to ABI handle + AbiHandleWrapper& operator=(AbiHandleWrapper&& other) + { + if (this != &other) + { + Destroy(); + + m_handle = std::move(other.m_handle); + m_free = other.m_free; + m_isValid = other.m_isValid; + + other.m_free = nullptr; + other.m_isValid = false; + } + + return *this; + } + + /// + /// Helper to simplify assigning a new ABI handle value to this wrapper + /// + /// The handle to assign + /// Reference to assigned handle + THandle& operator=(const THandle& other) + { + Destroy(); + + m_handle = other; + return m_handle; + } + + /// + /// Gets the address of the ABI handle. This is useful when calling ABI functions that set the value + /// + THandle* operator&() { return &m_handle; } + + /// + /// Gets the ABI handle value + /// + operator THandle() const { return m_handle; } + +private: + void Destroy() + { + if (m_isValid) + { + m_isValid = false; + if (m_free != nullptr) + { + m_free(m_handle); + } + } + } +}; + +/// +/// A wrapper around ABI handles +/// +using AbiHandle = AbiHandleWrapper; + +/// +/// A wrapper around strings allocated in the ABI layer +/// +using AbiStringHandle = AbiHandleWrapper; + +/// +/// Function that converts a handle to its underlying type. +/// +/// Handle type. +/// Object type. +/// Object from which to get the handle. +template +inline Handle HandleOrInvalid(std::shared_ptr obj) +{ + return obj == nullptr + ? static_cast(SPXHANDLE_INVALID) + : static_cast(*obj.get()); +} + + +template +struct TypeList {}; + +template class F, typename L> +struct TypeListIfAny; + +template class F> +struct TypeListIfAny> +{ + static constexpr bool value{ false }; +}; + +template class F, typename U, typename... Us> +struct TypeListIfAny> +{ + static constexpr bool value = F::value || Microsoft::CognitiveServices::Speech::Utils::TypeListIfAny>::value; +}; + +} } } } diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_info.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_info.h new file mode 100644 index 0000000..d220e4c --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_info.h @@ -0,0 +1,196 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_voice_info.h: Public API declarations for VoiceInfo C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { + +/// +/// Contains information about synthesis voice info +/// Updated in version 1.17.0 +/// +class VoiceInfo +{ +private: + + /// + /// Internal member variable that holds the voice info handle. + /// + SPXRESULTHANDLE m_hresult; + + /*! \cond PRIVATE */ + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXRESULTHANDLE hresult) : + PropertyCollection( + [=]() { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + voice_info_get_property_bag(hresult, &hpropbag); + return hpropbag; + }()) + { + } + }; + + /// + /// Internal member variable that holds the properties associating to the voice info. + /// + PrivatePropertyCollection m_properties; + + /*! \endcond */ + +public: + + /// + /// Creates a new instance using the provided handle. + /// + /// Result handle. + explicit VoiceInfo(SPXRESULTHANDLE hresult) : + m_hresult(hresult), + m_properties(hresult), + Name(m_name), + Locale(m_locale), + ShortName(m_shortName), + LocalName(m_localName), + Gender(m_gender), + VoiceType(m_voiceType), + StyleList(m_styleList), + VoicePath(m_voicePath), + Properties(m_properties) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + m_name = Utils::ToSPXString(Utils::CopyAndFreePropertyString(voice_info_get_name(m_hresult))); + m_locale = Utils::ToSPXString(Utils::CopyAndFreePropertyString(voice_info_get_locale(m_hresult))); + m_shortName = Utils::ToSPXString(Utils::CopyAndFreePropertyString(voice_info_get_short_name(m_hresult))); + m_localName = Utils::ToSPXString(Utils::CopyAndFreePropertyString(voice_info_get_local_name(m_hresult))); + m_styleList = Utils::Split(Utils::CopyAndFreePropertyString(voice_info_get_style_list(m_hresult)), '|'); + Synthesis_VoiceType voiceType; + SPX_THROW_ON_FAIL(voice_info_get_voice_type(hresult, &voiceType)); + m_voiceType = static_cast(voiceType); + m_voicePath = Utils::ToSPXString(Utils::CopyAndFreePropertyString(voice_info_get_voice_path(m_hresult))); + auto gender = Properties.GetProperty("Gender"); + m_gender = gender == "Female" ? SynthesisVoiceGender::Female : gender == "Male" ? SynthesisVoiceGender::Male : SynthesisVoiceGender::Unknown; + } + + /// + /// Explicit conversion operator. + /// + /// A handle. + explicit operator SPXRESULTHANDLE() { return m_hresult; } + + /// + /// Destructor. + /// + ~VoiceInfo() + { + voice_info_handle_release(m_hresult); + } + + /// + /// Voice name. + /// + const SPXSTRING& Name; + + /// + /// Locale of the voice. + /// + const SPXSTRING& Locale; + + /// + /// Short name. + /// + const SPXSTRING& ShortName; + + /// + /// Local name. + /// + const SPXSTRING& LocalName; + + /// + /// Gender. + /// Added in version 1.17.0 + /// + const SynthesisVoiceGender& Gender; + + /// + /// Local name. + /// + const SynthesisVoiceType& VoiceType; + + /// + /// Style list + /// + const std::vector& StyleList; + + /// + /// Voice path, only valid for offline voices. + /// + const SPXSTRING& VoicePath; + + /// + /// Collection of additional VoiceInfo properties. + /// + const PropertyCollection& Properties; + +private: + + DISABLE_DEFAULT_CTORS(VoiceInfo); + + /// + /// Internal member variable that holds the name. + /// + SPXSTRING m_name; + + /// + /// Internal member variable that holds the locale. + /// + SPXSTRING m_locale; + + /// + /// Internal member variable that holds the short name. + /// + SPXSTRING m_shortName; + + /// + /// Internal member variable that holds the local name. + /// + SPXSTRING m_localName; + + /// + /// Internal member variable that holds the gender. + /// + SynthesisVoiceGender m_gender; + + /// + /// Internal member variable that holds the voice type. + /// + SynthesisVoiceType m_voiceType; + + /// + /// Internal member variable that holds the style list. + /// + std::vector m_styleList; + + /// + /// Internal member variable that holds the voice path. + /// + SPXSTRING m_voicePath; +}; + + +} } } // Microsoft::CognitiveServices::Speech diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile.h new file mode 100644 index 0000000..78c8c38 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile.h @@ -0,0 +1,109 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_voice_profile.h: Public API declarations for VoiceProfile C++ class +// + +#pragma once +#include + +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Speaker { + +// Forward declaration for friends. +class VoiceProfileClient; + +/// +/// Class for VoiceProfile. +/// Added in version 1.12.0 +/// +class VoiceProfile : public std::enable_shared_from_this +{ +public: + + static std::shared_ptr FromId(const SPXSTRING& Id, VoiceProfileType voiceProfileType = VoiceProfileType::TextIndependentIdentification) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + SPXVOICEPROFILEHANDLE hVoiceProfile; + SPX_THROW_ON_FAIL(::create_voice_profile_from_id_and_type(&hVoiceProfile,Utils::ToUTF8(Id).c_str(), static_cast(voiceProfileType))); + return std::shared_ptr { new VoiceProfile(hVoiceProfile) }; + } + + /// + /// Destructor. + /// + virtual ~VoiceProfile() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + ::voice_profile_release_handle(m_hVoiceProfile); + m_hVoiceProfile = SPXHANDLE_INVALID; + } + + /// + /// Get a voice profile id. + /// + /// the voice profile id. + const SPXSTRING GetId() const + { + // query the string length + uint32_t length = 0; + SPX_THROW_ON_FAIL(voice_profile_get_id(m_hVoiceProfile, nullptr, &length)); + + // retrieve the string + std::unique_ptr buffer(new char[length]); + SPX_THROW_ON_FAIL(voice_profile_get_id(m_hVoiceProfile, buffer.get(), &length)); + return Utils::ToSPXString(buffer.get()); + } + + /// + /// Get the VoiceProfileType from the VoiceProfile. + /// + /// + VoiceProfileType GetType() const + { + int type = -1; + SPX_THROW_ON_FAIL(voice_profile_get_type(m_hVoiceProfile, &type)); + return static_cast(type); + } + + /// + /// Internal. Explicit conversion operator. + /// + /// A handle. + explicit operator SPXVOICEPROFILEHANDLE() { return m_hVoiceProfile; } + +protected: + + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Voice Profile handle. + explicit VoiceProfile(SPXVOICEPROFILEHANDLE hVoiceProfile) : + m_hVoiceProfile(hVoiceProfile) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + } + + /*! \endcond */ + +private: + + /*! \cond PRIVATE */ + friend Microsoft::CognitiveServices::Speech::Speaker::VoiceProfileClient; + DISABLE_DEFAULT_CTORS(VoiceProfile); + + SPXVOICEPROFILEHANDLE m_hVoiceProfile; + + /*! \endcond */ +}; + +} } } } // Microsoft::CognitiveServices::Speech::Speaker diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile_client.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile_client.h new file mode 100644 index 0000000..17b5dcd --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile_client.h @@ -0,0 +1,262 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_voice_profile_client.h: Public API declarations for VoiceProfileClient C++ class +// + +#pragma once +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Speaker { + +/// +/// Class for VoiceProfileClient. +/// This class creates voice profile client for creating, doing enrollment, deleting and reseting a voice profile. +/// Added in version 1.12.0 +/// +class VoiceProfileClient : public std::enable_shared_from_this +{ +private: + + /*! \cond PRIVATE */ + + SPXVOICEPROFILECLIENTHANDLE m_hVoiceProfileClient; + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXVOICEPROFILECLIENTHANDLE hclient) : + PropertyCollection( + [=]() { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + voice_profile_client_get_property_bag(hclient, &hpropbag); + return hpropbag; + }()) + { + } + }; + + PrivatePropertyCollection m_properties; + + /*! \endcond */ + +public: + + /// + /// Create a Voice Profile Client from a speech config + /// + /// Speech configuration. + /// A smart pointer wrapped voice profile client pointer. + static std::shared_ptr FromConfig(std::shared_ptr speechConfig) + { + SPXVOICEPROFILECLIENTHANDLE hVoiceProfileClient; + SPX_THROW_ON_FAIL(::create_voice_profile_client_from_config(&hVoiceProfileClient, Utils::HandleOrInvalid(speechConfig))); + return std::shared_ptr{ new VoiceProfileClient(hVoiceProfileClient)}; + } + + /// + /// Destructor. + /// + virtual ~VoiceProfileClient() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + ::voice_profile_client_release_handle(m_hVoiceProfileClient); + m_hVoiceProfileClient = SPXHANDLE_INVALID; + } + + /// + /// Create a Voice Profile. + /// + /// a VoiceProfile type. + /// a locale, e.g "en-us" + /// A smart pointer wrapped voice profile client object. + std::future> CreateProfileAsync(VoiceProfileType profileType, const SPXSTRING& locale) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [profileType, locale, this, keepAlive]() -> std::shared_ptr { + SPXVOICEPROFILEHANDLE hVoiceProfileHandle; + SPX_THROW_ON_FAIL(::create_voice_profile(m_hVoiceProfileClient, static_cast(profileType), Utils::ToUTF8(locale).c_str(), &hVoiceProfileHandle)); + return std::shared_ptr { new VoiceProfile(hVoiceProfileHandle) }; + }); + + return future; + } + + /// + /// Enroll a Voice Profile. + /// + /// a voice profile object. + /// an audio Input. + /// A smart pointer wrapped voice profile enrollment result object. + std::future> EnrollProfileAsync(std::shared_ptr profile, std::shared_ptr audioInput = nullptr) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [profile, audioInput, this, keepAlive]() -> std::shared_ptr { + SPXRESULTHANDLE hresult; + SPX_THROW_ON_FAIL(::enroll_voice_profile(m_hVoiceProfileClient, + Utils::HandleOrInvalid(profile), + Utils::HandleOrInvalid(audioInput), + &hresult)); + return std::make_shared(hresult); + }); + return future; + } + + /// + /// Delete a Voice Profile. + /// + /// a voice profile object. + /// A smart pointer wrapped voice profile result object. + std::future> DeleteProfileAsync(std::shared_ptr profile) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [profile, this, keepAlive]() -> std::shared_ptr { + SPXRESULTHANDLE hResultHandle; + SPX_THROW_ON_FAIL(::delete_voice_profile(m_hVoiceProfileClient, + Utils::HandleOrInvalid(profile), + &hResultHandle)); + return std::make_shared(hResultHandle); + }); + return future; + } + + /// + /// Reset a Voice Profile. + /// + /// a voice profile object. + /// A smart pointer wrapped voice profile result object. + std::future> ResetProfileAsync(std::shared_ptr profile) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [profile, this, keepAlive]() -> std::shared_ptr { + SPXRESULTHANDLE hResultHandle; + SPX_THROW_ON_FAIL(::reset_voice_profile(m_hVoiceProfileClient, + Utils::HandleOrInvalid(profile), + &hResultHandle)); + return std::make_shared(hResultHandle); + }); + return future; + } + + /// + /// Retrieve an enrollment result given the id and type of the Voice Profile. + /// + /// The VoiceProfile Id. + /// The VoiceProfileType. + /// A future of the retrieved VoiceProfileEnrollmentResult. + std::future> RetrieveEnrollmentResultAsync(const SPXSTRING& voiceProfileId, VoiceProfileType voiceProfileType) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [voiceProfileId, voiceProfileType, this, keepAlive]() -> std::shared_ptr { + SPXRESULTHANDLE hResultHandle; + SPX_THROW_ON_FAIL(::retrieve_enrollment_result(m_hVoiceProfileClient, Utils::ToUTF8(voiceProfileId).c_str(), static_cast(voiceProfileType), &hResultHandle)); + return std::make_shared(hResultHandle); + }); + return future; + } + + /// + /// Retrieve an enrollment result given the Voice Profile. + /// + /// a voice profile object. + /// + std::future> RetrieveEnrollmentResultAsync(const VoiceProfile& voiceProfile) + { + return RetrieveEnrollmentResultAsync(voiceProfile.GetId(), voiceProfile.GetType()); + } + + /// + /// Get all profiles having the given type. + /// + /// The VoiceProfileType. + /// A future of a vector of extant VoiceProfiles. + std::future>> GetAllProfilesAsync(VoiceProfileType voiceProfileType) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [voiceProfileType, this, keepAlive]() -> std::vector> + { + std::vector> list; + + size_t numChars = 0; + char* json = nullptr; + auto deleteJsonOnEixt = Utils::MakeScopeGuard([&json]() { + ::property_bag_free_string(json); + }); + + SPX_THROW_ON_FAIL(::get_profiles_json(m_hVoiceProfileClient, static_cast(voiceProfileType), &json, &numChars)); + + auto profileList = Utils::Split(json, numChars, '|'); + for (auto& profile: profileList) + { + list.push_back(VoiceProfile::FromId(Utils::ToSPXString(profile), voiceProfileType)); + } + + return list; + }); + return future; + } + + std::future> GetActivationPhrasesAsync(VoiceProfileType voiceProfileType, const SPXSTRING& locale) + { + auto keepAlive = this->shared_from_this(); + auto future = std::async(std::launch::async, [voiceProfileType, locale, this, keepAlive]() -> std::shared_ptr { + SPXRESULTHANDLE hresult; + SPX_THROW_ON_FAIL(::get_activation_phrases(m_hVoiceProfileClient, + Utils::ToUTF8(locale).c_str(), + static_cast(voiceProfileType), + &hresult)); + return std::make_shared(hresult); + }); + return future; + } + /// + /// A collection of properties and their values defined for this . + /// + PropertyCollection& Properties; + + /// + /// Internal. Explicit conversion operator. + /// + /// A handle. + explicit operator SPXVOICEPROFILECLIENTHANDLE() { return m_hVoiceProfileClient; } + +protected: + + /*! \cond PROTECTED */ + + /// + /// Internal constructor. Creates a new instance using the provided handle. + /// + /// Recognizer handle. + explicit VoiceProfileClient(SPXVOICEPROFILECLIENTHANDLE hVoiceProfileClient) : + m_hVoiceProfileClient(hVoiceProfileClient), + m_properties(hVoiceProfileClient), + Properties(m_properties) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + } + + /*! \endcond */ + +private: + + DISABLE_DEFAULT_CTORS(VoiceProfileClient); +}; + +} } } } // Microsoft::CognitiveServices::Speech::Speaker diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile_enrollment_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile_enrollment_result.h new file mode 100644 index 0000000..22decb5 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile_enrollment_result.h @@ -0,0 +1,242 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_voice_profile_enrollment_result.h: Public API declarations for VoiceProfileEnrollmentResult C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Speaker { + +/// +/// A enum that represents the timing information of an enrollment. +/// Added in version 1.12.0. +/// +enum class EnrollmentInfoType +{ + /// + /// Number of enrollment audios accepted for this profile. + /// + EnrollmentsCount = 0, + + /// + /// Total length of enrollment audios accepted for this profile. + /// + EnrollmentsLength = 1, + + /// + /// Summation of pure speech(which is the amount of audio after removing silence and non - speech segments) across all profile enrollments. + /// + EnrollmentsSpeechLength = 2, + + /// + /// Amount of pure speech (which is the amount of audio after removing silence and non-speech segments) needed to complete profile enrollment. + /// + RemainingEnrollmentsSpeechLength = 3, + + /// + /// Number of enrollment audios needed to complete profile enrollment. + /// + RemainingEnrollmentsCount = 4, + + /// + /// This enrollment audio length in hundred nanoseconds. + /// + AudioLength = 5, + + /// + /// This enrollment audio pure speech(which is the amount of audio after removing silence and non - speech segments) length in hundred nanoseconds. + /// + AudioSpeechLength = 6 +}; + +/// +/// Represents the result of an enrollment. +/// Added in version 1.12.0. +/// +class VoiceProfileEnrollmentResult final : public RecognitionResult +{ +private: + + SPXSTRING m_profileId; + const int enrollmentsCount; + const uint64_t enrollmentsLength; + const uint64_t enrollmentsSpeechLength; + const int remainingEnrollmentsCount; + const uint64_t remainingEnrollmentsSpeechLength; + const uint64_t audioLength; + const uint64_t audioSpeechLength; + const SPXSTRING createdDateTime; + const SPXSTRING lastUpdatedDateTime; + +public: + + /// + /// Creates a new instance using the provided handle. + /// + /// Result handle. + explicit VoiceProfileEnrollmentResult(SPXRESULTHANDLE hresult) : + RecognitionResult(hresult), + m_profileId(Properties.GetProperty("enrollment.profileId", "")), + enrollmentsCount(std::stoi(Properties.GetProperty("enrollment.enrollmentsCount", "0"))), + enrollmentsLength(static_cast(std::stoll(Properties.GetProperty("enrollment.enrollmentsLengthInSec", "0")))), + enrollmentsSpeechLength(static_cast(std::stoll(Properties.GetProperty("enrollment.enrollmentsSpeechLengthInSec", "0")))), + remainingEnrollmentsCount(std::stoi(Properties.GetProperty("enrollment.remainingEnrollmentsCount", "0"))), + remainingEnrollmentsSpeechLength(std::stoll(Properties.GetProperty("enrollment.remainingEnrollmentsSpeechLengthInSec", "0"))), + audioLength(static_cast(std::stoll(Properties.GetProperty("enrollment.audioLengthInSec", "0")))), + audioSpeechLength(static_cast(std::stoll(Properties.GetProperty("enrollment.audioSpeechLengthInSec", "0")))), + createdDateTime(Properties.GetProperty("enrollment.createdDateTime", "")), + lastUpdatedDateTime(Properties.GetProperty("enrollment.lastUpdatedDateTime", "")), + ProfileId(m_profileId) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + } + + /// + /// Destructor. + /// + virtual ~VoiceProfileEnrollmentResult() + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + } + + /// + /// The profile id of the speaker in the enrollment. + /// + const SPXSTRING& ProfileId; + + /// + /// Retrieve a textual representation of the created time of the voice profile. + /// + /// + const SPXSTRING& GetCreatedTime() const + { + return createdDateTime; + } + + /// + /// Retrieve a textual representation of the last updated time of the voice profile. + /// + /// + const SPXSTRING& GetLastUpdatedDateTime() const + { + return lastUpdatedDateTime; + } + + /// + /// Enrollment information in ticks. + /// A single tick represents one hundred nanoseconds or one ten-millionth of a second. + /// + /// an enum of EnrollmentInfoType. + /// Duration of recognized speech in ticks. + uint64_t GetEnrollmentInfo(EnrollmentInfoType type) const + { + switch (type) + { + case EnrollmentInfoType::EnrollmentsCount: + return static_cast(enrollmentsCount); + + case EnrollmentInfoType::EnrollmentsLength: + return enrollmentsLength; + + case EnrollmentInfoType::EnrollmentsSpeechLength: + return enrollmentsSpeechLength; + + case EnrollmentInfoType::RemainingEnrollmentsCount: + return static_cast(remainingEnrollmentsCount); + + case EnrollmentInfoType::RemainingEnrollmentsSpeechLength: + return remainingEnrollmentsSpeechLength; + + case EnrollmentInfoType::AudioLength: + return audioLength; + + case EnrollmentInfoType::AudioSpeechLength: + return audioSpeechLength; + + default: + throw std::runtime_error("Invalid enrollmentInfoType!"); + } + } + +private: + + DISABLE_DEFAULT_CTORS(VoiceProfileEnrollmentResult); + +}; + +/// +/// Represents the cancellation details of a result of an enrollment. +/// Added in version 1.12.0. +/// +class VoiceProfileEnrollmentCancellationDetails +{ +private: + + CancellationErrorCode m_errorCode; + +public: + + /// + /// Create an object that represents the details of a canceled enrollment result. + /// + /// a voice profile enrollment result object. + /// a smart pointer of voice profile enrollment cancellation details object. + static std::shared_ptr FromResult(std::shared_ptr result) + { + return std::shared_ptr { new VoiceProfileEnrollmentCancellationDetails(result.get()) }; + } + + /// + /// The error code in case of an unsuccessful enrollment ( is set to Error). + /// + const CancellationErrorCode& ErrorCode; + + /// + /// The error message in case of an unsuccessful enrollment ( is set to Error). + /// + const SPXSTRING ErrorDetails; + +protected: + + /*! \cond PROTECTED */ + + VoiceProfileEnrollmentCancellationDetails(VoiceProfileEnrollmentResult* result) : + m_errorCode(GetCancellationErrorCode(result)), + ErrorCode(m_errorCode), + ErrorDetails(result->Properties.GetProperty(PropertyId::SpeechServiceResponse_JsonErrorDetails)) + { + } + + /*! \endcond */ + +private: + DISABLE_DEFAULT_CTORS(VoiceProfileEnrollmentCancellationDetails); + + /*! \cond PRIVATE */ + + CancellationErrorCode GetCancellationErrorCode(VoiceProfileEnrollmentResult* result) + { + Result_CancellationErrorCode errorCode = CancellationErrorCode_NoError; + + SPXRESULTHANDLE hresult = (SPXRESULTHANDLE)(*result); + SPX_IFFAILED_THROW_HR(result_get_canceled_error_code(hresult, &errorCode)); + + return static_cast(errorCode); + } + + /*! \endcond */ +}; + +} } } } // Microsoft::CognitiveServices::Speech::Speaker diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile_phrase_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile_phrase_result.h new file mode 100644 index 0000000..5695413 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile_phrase_result.h @@ -0,0 +1,194 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_speech_voice_profile_phrase_result.h: Public API declarations for VoiceProfilePhraseResult C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace Microsoft { + namespace CognitiveServices { + namespace Speech { + namespace Speaker { + + /// + /// Class for VoiceProfilePhraseResult. + /// This class represents the result of requesting valid activation phrases for speaker recognition. + /// Added in version 1.18.0 + /// + class VoiceProfilePhraseResult + { + private: + + /*! \cond PRIVATE */ + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXRESULTHANDLE hresult) : + PropertyCollection( + [=]() { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + result_get_property_bag(hresult, &hpropbag); + return hpropbag; + }()) + { + } + }; + + PrivatePropertyCollection m_properties; + + /*! \endcond */ + + public: + explicit VoiceProfilePhraseResult(SPXRESULTHANDLE hresult) : + m_properties(hresult), + ResultId(m_resultId), + Reason(m_reason), + Properties(m_properties), + m_phrases(std::make_shared>(Utils::Split(m_properties.GetProperty("speakerrecognition.phrases", ""), '|'))), + m_hresult(hresult) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + PopulateResultFields(hresult, &m_resultId, &m_reason); + } + + virtual ~VoiceProfilePhraseResult() + { + ::recognizer_result_handle_release(m_hresult); + m_hresult = SPXHANDLE_INVALID; + } + + /// + /// Unique result id. + /// + const SPXSTRING& ResultId; + + /// + /// Voice profile result reason. + /// + const ResultReason& Reason; + + /// + /// A collection of properties and their values defined for this . + /// + PropertyCollection& Properties; + + /// + /// Gets the activation phrases. + /// + /// Vector of phrases in string form + std::shared_ptr> GetPhrases() + { + return m_phrases; + } + + /// + /// Internal. Explicit conversion operator. + /// + /// A handle. + explicit operator SPXRESULTHANDLE() { return m_hresult; } + + private: + DISABLE_DEFAULT_CTORS(VoiceProfilePhraseResult); + + void PopulateResultFields(SPXRESULTHANDLE hresult, SPXSTRING* resultId, Speech::ResultReason* reason) + { + SPX_INIT_HR(hr); + + const size_t maxCharCount = 2048; + char sz[maxCharCount + 1] = {}; + + if (resultId != nullptr) + { + SPX_THROW_ON_FAIL(hr = result_get_result_id(hresult, sz, maxCharCount)); + *resultId = Utils::ToSPXString(sz); + } + + if (reason != nullptr) + { + Result_Reason resultReason; + SPX_THROW_ON_FAIL(hr = result_get_reason(hresult, &resultReason)); + *reason = (Speech::ResultReason)resultReason; + } + } + + ResultReason m_reason; + SPXSTRING m_resultId; + std::shared_ptr> m_phrases; + SPXRESULTHANDLE m_hresult; + }; + + /// + /// Class for VoiceProfilePhraseCancellationDetails. + /// This class represents error details of a voice profile result. + /// + class VoiceProfilePhraseCancellationDetails + { + private: + CancellationErrorCode m_errorCode; + + public: + + /// + /// Creates an instance of VoiceProfilePhraseCancellationDetails object for the canceled VoiceProfile. + /// + /// The result that was canceled. + /// A shared pointer to VoiceProfilePhraseCancellationDetails. + static std::shared_ptr FromResult(std::shared_ptr result) + { + return std::shared_ptr { new VoiceProfilePhraseCancellationDetails(result.get()) }; + } + + /// + /// The error code in case of an unsuccessful voice profile action( is set to Error). + /// If Reason is not Error, ErrorCode is set to NoError. + /// + const CancellationErrorCode& ErrorCode; + + /// + /// The error message in case of an unsuccessful voice profile action( is set to Error). + /// + const SPXSTRING ErrorDetails; + + protected: + + /*! \cond PROTECTED */ + + VoiceProfilePhraseCancellationDetails(VoiceProfilePhraseResult* result) : + m_errorCode(GetCancellationErrorCode(result)), + ErrorCode(m_errorCode), + ErrorDetails(result->Properties.GetProperty(PropertyId::SpeechServiceResponse_JsonErrorDetails)) + { + } + + /*! \endcond */ + + private: + DISABLE_DEFAULT_CTORS(VoiceProfilePhraseCancellationDetails); + + + CancellationErrorCode GetCancellationErrorCode(VoiceProfilePhraseResult* result) + { + UNUSED(result); + Result_CancellationErrorCode errorCode = CancellationErrorCode_NoError; + + SPXRESULTHANDLE hresult = (SPXRESULTHANDLE)(*result); + SPX_IFFAILED_THROW_HR(result_get_canceled_error_code(hresult, &errorCode)); + + return (CancellationErrorCode)errorCode; + } + }; + + } + } + } +} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile_result.h b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile_result.h new file mode 100644 index 0000000..83d0db1 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/include/cxx_api/speechapi_cxx_voice_profile_result.h @@ -0,0 +1,180 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// See https://aka.ms/csspeech/license for the full license information. +// +// speechapi_cxx_speech_voice_profile_result.h: Public API declarations for VoiceProfileResult C++ class +// + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace Microsoft { +namespace CognitiveServices { +namespace Speech { +namespace Speaker { + +/// +/// Class for VoiceProfileResult. +/// This class represents the result of processing voice profiles. +/// Added in version 1.12.0 +/// +class VoiceProfileResult +{ +private: + + /*! \cond PRIVATE */ + + class PrivatePropertyCollection : public PropertyCollection + { + public: + PrivatePropertyCollection(SPXRESULTHANDLE hresult) : + PropertyCollection( + [=]() { + SPXPROPERTYBAGHANDLE hpropbag = SPXHANDLE_INVALID; + result_get_property_bag(hresult, &hpropbag); + return hpropbag; + }()) + { + } + }; + + PrivatePropertyCollection m_properties; + + /*! \endcond */ + +public: + explicit VoiceProfileResult(SPXRESULTHANDLE hresult) : + m_properties(hresult), + ResultId(m_resultId), + Reason(m_reason), + Properties(m_properties), + m_hresult(hresult) + { + SPX_DBG_TRACE_SCOPE(__FUNCTION__, __FUNCTION__); + + PopulateResultFields(hresult, &m_resultId, &m_reason); + } + + virtual ~VoiceProfileResult() + { + ::recognizer_result_handle_release(m_hresult); + m_hresult = SPXHANDLE_INVALID; + } + + /// + /// Unique result id. + /// + const SPXSTRING& ResultId; + + /// + /// Voice profile result reason. + /// + const ResultReason& Reason; + + /// + /// A collection of properties and their values defined for this . + /// + PropertyCollection& Properties; + + /// + /// Internal. Explicit conversion operator. + /// + /// A handle. + explicit operator SPXRESULTHANDLE() { return m_hresult; } + +private: + DISABLE_DEFAULT_CTORS(VoiceProfileResult); + + void PopulateResultFields(SPXRESULTHANDLE hresult, SPXSTRING* resultId, Speech::ResultReason* reason) + { + SPX_INIT_HR(hr); + + const size_t maxCharCount = 2048; + char sz[maxCharCount + 1] = {}; + + if (resultId != nullptr) + { + SPX_THROW_ON_FAIL(hr = result_get_result_id(hresult, sz, maxCharCount)); + *resultId = Utils::ToSPXString(sz); + } + + if (reason != nullptr) + { + Result_Reason resultReason; + SPX_THROW_ON_FAIL(hr = result_get_reason(hresult, &resultReason)); + *reason = (Speech::ResultReason)resultReason; + } + } + + ResultReason m_reason; + SPXSTRING m_resultId; + SPXRESULTHANDLE m_hresult; +}; + +/// +/// Class for VoiceProfileCancellationDetails. +/// This class represents error details of a voice profile result. +/// +class VoiceProfileCancellationDetails +{ +private: + CancellationErrorCode m_errorCode; + +public: + + /// + /// Creates an instance of VoiceProfileCancellationDetails object for the canceled VoiceProfile. + /// + /// The result that was canceled. + /// A shared pointer to VoiceProfileCancellationDetails. + static std::shared_ptr FromResult(std::shared_ptr result) + { + return std::shared_ptr { new VoiceProfileCancellationDetails(result.get()) }; + } + + /// + /// The error code in case of an unsuccessful voice profile action( is set to Error). + /// If Reason is not Error, ErrorCode is set to NoError. + /// + const CancellationErrorCode& ErrorCode; + + /// + /// The error message in case of an unsuccessful voice profile action( is set to Error). + /// + const SPXSTRING ErrorDetails; + +protected: + + /*! \cond PROTECTED */ + + VoiceProfileCancellationDetails(VoiceProfileResult* result) : + m_errorCode(GetCancellationErrorCode(result)), + ErrorCode(m_errorCode), + ErrorDetails(result->Properties.GetProperty(PropertyId::SpeechServiceResponse_JsonErrorDetails)) + { + } + + /*! \endcond */ + +private: + DISABLE_DEFAULT_CTORS(VoiceProfileCancellationDetails); + + + CancellationErrorCode GetCancellationErrorCode(VoiceProfileResult* result) + { + UNUSED(result); + Result_CancellationErrorCode errorCode = CancellationErrorCode_NoError; + + SPXRESULTHANDLE hresult = (SPXRESULTHANDLE)(*result); + SPX_IFFAILED_THROW_HR(result_get_canceled_error_code(hresult, &errorCode)); + + return (CancellationErrorCode)errorCode; + } +}; + +} } } } // Microsoft::CognitiveServices::Speech::Speaker diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/libs/Microsoft.CognitiveServices.Speech.core.lib b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/libs/Microsoft.CognitiveServices.Speech.core.lib new file mode 100644 index 0000000000000000000000000000000000000000..4d5b5b816f6f3ec52b69a4eb04d44fbeb4a4eb61 GIT binary patch literal 200466 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�u0rJo}cWdYboj*Keo3%ji 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 E0ZWNvUH||9 literal 0 HcmV?d00001 diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/libs/Runtime/Microsoft.CognitiveServices.Speech.core.dll b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/AzureWrapper/libs/Runtime/Microsoft.CognitiveServices.Speech.core.dll new file mode 100644 index 0000000000000000000000000000000000000000..36ab3d970b1fb31e7bc7af3b7504f02c88c1a985 GIT binary patch literal 2286632 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

=!&~E%i_gzi)a3K!(QdjAyYRHG6HoImYvTFx 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

ike0svtit!21u&<2tYt_ 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 z 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

_P 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