From 4c526aeb59512fbcfd04eba89fef26b7101c95e5 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 10 Jun 2022 13:47:30 +0900 Subject: [PATCH 1/6] [flutter_tts] Refactor the C++ code - Extract method. - Hiding tizen api in plugin.cc - Enhance error message handling (working) - Minimize use of output parameters - Change method name(Init-> TtsCreate, DeInit -> TtsDestroy, ...) --- packages/flutter_tts/CHANGELOG.md | 4 + packages/flutter_tts/pubspec.yaml | 2 +- .../tizen/inc/flutter_tts_tizen_plugin.h | 4 + .../tizen/src/flutter_tts_tizen_plugin.cc | 280 ++++++++++++------ .../flutter_tts/tizen/src/text_to_speech.cc | 83 +++--- .../flutter_tts/tizen/src/text_to_speech.h | 57 ++-- 6 files changed, 266 insertions(+), 164 deletions(-) diff --git a/packages/flutter_tts/CHANGELOG.md b/packages/flutter_tts/CHANGELOG.md index 4e2fef778..a89a57295 100644 --- a/packages/flutter_tts/CHANGELOG.md +++ b/packages/flutter_tts/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.2.1 + +* Refactor the C++ code. + ## 1.2.0 * Fix a bug where `setLanguage()` wasn't invoked due to typo in `setLanguage`. diff --git a/packages/flutter_tts/pubspec.yaml b/packages/flutter_tts/pubspec.yaml index 58461e2d9..5b443d38e 100644 --- a/packages/flutter_tts/pubspec.yaml +++ b/packages/flutter_tts/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_tts_tizen description: The tizen implementation of flutter_tts plugin. homepage: https://github.com/flutter-tizen/plugins repository: https://github.com/flutter-tizen/plugins/tree/master/packages/flutter_tts -version: 1.2.0 +version: 1.2.1 dependencies: flutter: diff --git a/packages/flutter_tts/tizen/inc/flutter_tts_tizen_plugin.h b/packages/flutter_tts/tizen/inc/flutter_tts_tizen_plugin.h index c89564803..9f62163c6 100644 --- a/packages/flutter_tts/tizen/inc/flutter_tts_tizen_plugin.h +++ b/packages/flutter_tts/tizen/inc/flutter_tts_tizen_plugin.h @@ -1,3 +1,7 @@ +// Copyright 2021 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + #ifndef FLUTTER_PLUGIN_FLUTTER_TTS_TIZEN_PLUGIN_H_ #define FLUTTER_PLUGIN_FLUTTER_TTS_TIZEN_PLUGIN_H_ diff --git a/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc b/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc index 565f1ad88..7dd3c8718 100644 --- a/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc +++ b/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc @@ -8,15 +8,18 @@ #include #include -#include #include -#include #include #include #include "log.h" #include "text_to_speech.h" +namespace { + +typedef flutter::MethodChannel FlMethodChannel; +typedef flutter::MethodResult FlMethodResult; + class FlutterTtsTizenPlugin : public flutter::Plugin { public: static void RegisterWithRegistrar(flutter::PluginRegistrar *registrar) { @@ -25,15 +28,25 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { } FlutterTtsTizenPlugin(flutter::PluginRegistrar *registrar) { - channel_ = - std::make_unique>( - registrar->messenger(), "flutter_tts", - &flutter::StandardMethodCodec::GetInstance()); + channel_ = std::make_unique( + registrar->messenger(), "flutter_tts", + &flutter::StandardMethodCodec::GetInstance()); channel_->SetMethodCallHandler([this](const auto &call, auto result) { this->HandleMethodCall(call, std::move(result)); }); tts_ = std::make_unique(); + try { + tts_->Initialize(); + } catch (const TextToSpeechError &error) { + // TODO : Handle initialization failure cases + // Rarely, initializing TextToSpeech can fail. we should consider catching + // the exception and propagating it to the flutter side. however, I think + // this is optional because flutter side is not expecting any errors. + LOG_ERROR("Operation failed : %s", error.GetErrorString().c_str()); + tts_ = nullptr; + return; + } tts_->SetOnStateChanagedCallback( [this](tts_state_e previous, tts_state_e current) -> void { std::unique_ptr value = @@ -76,108 +89,177 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { void HandleMethodCall( const flutter::MethodCall &method_call, std::unique_ptr> result) { - // Keep in sync with the return values implemented in: - // https://github.com/dlutton/flutter_tts/blob/master/android/src/main/java/com/tundralabs/fluttertts/FlutterTtsPlugin.java. - // In principle, MethodResult was designed to call MethodResult.Error() to - // notify the dart code of any method call failures from the host platform. - // However, in the case of flutter_tts, it expects a return value 0 on - // failure(and value 1 on success). Therefore in the scope of this plugin, - // we call result->Success(flutter::EncodableValue(0)) to notify errors. - const auto method_name = method_call.method_name(); const auto &arguments = *method_call.arguments(); - if (method_name.compare("awaitSpeakCompletion") == 0) { - if (std::holds_alternative(arguments)) { - await_speak_completion_ = std::get(arguments); - result->Success(flutter::EncodableValue(1)); - return; - } - result->Success(flutter::EncodableValue(0)); - } else if (method_name.compare("speak") == 0) { - if (tts_->GetState() == TTS_STATE_PLAYING) { - LOG_ERROR("[TTS] : You cannot speak again while speaking."); - result->Success(flutter::EncodableValue(0)); + result_ = std::move(result); + + if (!tts_) { + result_->Error("Operation failed", "TTS is invalid."); + return; + } + + if (method_name == "awaitSpeakCompletion") { + OnAwaitSpeakCompletion(arguments); + } else if (method_name == "speak") { + OnSpeak(arguments); + } else if (method_name == "stop") { + OnStop(); + } else if (method_name == "pause") { + OnPause(); + } else if (method_name == "getSpeechRateValidRange") { + OnGetSpeechRateValidRange(); + } else if (method_name == "setSpeechRate") { + OnSetSpeechRate(arguments); + } else if (method_name == "setLanguage") { + OnSetLanguage(arguments); + } else if (method_name == "getLanguages") { + OnGetLanguage(); + } else if (method_name == "setVolume") { + OnSetVolume(arguments); + } else { + result_->NotImplemented(); + } + } + + void OnAwaitSpeakCompletion(const flutter::EncodableValue &arguments) { + if (std::holds_alternative(arguments)) { + await_speak_completion_ = std::get(arguments); + SendResult(flutter::EncodableValue(1)); + return; + } + SendErrorResult("Invalid argument", "Argument is invaild."); + } + + void OnSpeak(const flutter::EncodableValue &arguments) { + try { + if (tts_->GetState() == TtsState::kPlaying) { + SendErrorResult("Operation cancelled", + "You cannot speak again while speaking."); return; } if (std::holds_alternative(arguments)) { std::string text = std::get(arguments); - if (!tts_->AddText(text)) { - result->Success(flutter::EncodableValue(0)); - return; - } + tts_->AddText(text); } - if (tts_->Speak()) { - if (await_speak_completion_ && !result_for_await_speak_completion_) { - LOG_DEBUG("Store result ptr for await speak completion"); - result_for_await_speak_completion_ = std::move(result); - } else { - result->Success(flutter::EncodableValue(1)); - } - } else { - result->Success(flutter::EncodableValue(0)); - } - } else if (method_name.compare("stop") == 0) { - if (tts_->Stop()) { - result->Success(flutter::EncodableValue(1)); - } else { - result->Success(flutter::EncodableValue(0)); - } - } else if (method_name.compare("pause") == 0) { - if (tts_->Pause()) { - result->Success(flutter::EncodableValue(1)); - } else { - result->Success(flutter::EncodableValue(0)); - } - } else if (method_name.compare("getSpeechRateValidRange") == 0) { - int min = 0, normal = 0, max = 0; + tts_->Speak(); + + } catch (const TextToSpeechError &error) { + SendErrorResult("Operation failed", error.GetErrorString()); + } + + if (await_speak_completion_ && !result_for_await_speak_completion_) { + LOG_DEBUG("Store result ptr for await speak completion"); + result_for_await_speak_completion_ = std::move(result_); + } else { + SendResult(flutter::EncodableValue(1)); + } + } + + void OnStop() { + try { + tts_->Stop(); + } catch (const TextToSpeechError &error) { + SendErrorResult("Operation failed", error.GetErrorString()); + } + SendResult(flutter::EncodableValue(1)); + } + + void OnPause() { + try { + tts_->Pause(); + } catch (const TextToSpeechError &error) { + SendErrorResult("Operation failed", error.GetErrorString()); + } + SendResult(flutter::EncodableValue(1)); + } + + void OnGetSpeechRateValidRange() { + int min = 0, normal = 0, max = 0; + try { tts_->GetSpeedRange(&min, &normal, &max); - flutter::EncodableMap map; - map.insert(std::pair( - "min", min)); - map.insert(std::pair( - "normal", normal)); - map.insert(std::pair( - "max", max)); - map.insert(std::pair( - "platform", "tizen")); - result->Success(flutter::EncodableValue(std::move(map))); - } else if (method_name.compare("setSpeechRate") == 0) { - if (std::holds_alternative(arguments)) { - int speed = (int)std::get(arguments); - tts_->SetTtsSpeed(speed); - result->Success(flutter::EncodableValue(1)); - return; - } - result->Success(flutter::EncodableValue(0)); - } else if (method_name.compare("setLanguage") == 0) { - if (std::holds_alternative(arguments)) { - std::string language = std::move(std::get(arguments)); - tts_->SetDefaultLanguage(language); - result->Success(flutter::EncodableValue(1)); + } catch (const TextToSpeechError &error) { + SendErrorResult("Operation failed", error.GetErrorString()); + return; + } + flutter::EncodableMap map; + map.insert(std::pair( + "min", min)); + map.insert(std::pair( + "normal", normal)); + map.insert(std::pair( + "max", max)); + map.insert(std::pair( + "platform", "tizen")); + SendResult(flutter::EncodableValue(std::move(map))); + } + + void OnSetSpeechRate(const flutter::EncodableValue &arguments) { + if (std::holds_alternative(arguments)) { + int speed = (int)std::get(arguments); + tts_->SetTtsSpeed(speed); + SendResult(flutter::EncodableValue(1)); + return; + } + SendErrorResult("Invalid argument", "SpeechRate is invaild."); + } + + void OnSetLanguage(const flutter::EncodableValue &arguments) { + if (std::holds_alternative(arguments)) { + std::string language = std::move(std::get(arguments)); + tts_->SetDefaultLanguage(language); + SendResult(flutter::EncodableValue(1)); + return; + } + SendErrorResult("Invalid argument", "Language is invaild."); + } + + void OnGetLanguage() { + flutter::EncodableList list; + for (auto language : tts_->GetSupportedLanaguages()) { + list.push_back(flutter::EncodableValue(language)); + } + SendResult(flutter::EncodableValue(list)); + } + + void OnSetVolume(const flutter::EncodableValue &arguments) { + if (std::holds_alternative(arguments)) { + double volume = std::get(arguments); + try { + tts_->SetVolume(volume); + } catch (const TextToSpeechError &error) { + SendErrorResult("Operation failed", error.GetErrorString()); return; } - result->Success(flutter::EncodableValue(0)); - } else if (method_name.compare("getLanguages") == 0) { - flutter::EncodableList list; - for (auto language : tts_->GetSupportedLanaguages()) { - list.push_back(flutter::EncodableValue(language)); - } - result->Success(flutter::EncodableValue(list)); - } else if (method_name.compare("setVolume") == 0) { - if (std::holds_alternative(arguments)) { - double rate = std::get(arguments); - if (tts_->SetVolume(rate)) { - result->Success(flutter::EncodableValue(1)); - return; - } - } - result->Success(flutter::EncodableValue(0)); - } else { - result->Error("-1", "Not supported method"); } + SendErrorResult("Invalid argument", "Volume is invaild."); + } + + void SendResult(const flutter::EncodableValue &result) { + if (!result_) { + return; + } + result_->Success(result); + result_ = nullptr; + } + + void SendErrorResult(const std::string &error_code, + const std::string &error_message) { + // Keep in sync with the return values implemented in: + // https://github.com/dlutton/flutter_tts/blob/master/android/src/main/java/com/tundralabs/fluttertts/FlutterTtsPlugin.java. + // In principle, MethodResult was designed to call MethodResult.Error() to + // notify the dart code of any method call failures from the host platform. + // However, in the case of flutter_tts, it expects a return value 0 on + // failure(and value 1 on success). Therefore in the scope of this plugin, + // we call result_->Success(flutter::EncodableValue(0)) to notify errors. + if (!result_) { + return; + } + LOG_ERROR("%s", std::string(error_code + " : " + error_message).c_str()); + result_->Success(flutter::EncodableValue(0)); + result_ = nullptr; } void HandleAwaitSpeakCompletion(int value) { @@ -190,13 +272,15 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { } std::unique_ptr tts_; - std::unique_ptr> channel_; - bool await_speak_completion_ = false; - std::unique_ptr> - result_for_await_speak_completion_; + + std::unique_ptr result_for_await_speak_completion_; + std::unique_ptr result_; + std::unique_ptr channel_; }; +} // namespace + void FlutterTtsTizenPluginRegisterWithRegistrar( FlutterDesktopPluginRegistrarRef registrar) { FlutterTtsTizenPlugin::RegisterWithRegistrar( diff --git a/packages/flutter_tts/tizen/src/text_to_speech.cc b/packages/flutter_tts/tizen/src/text_to_speech.cc index 70a141a71..9b0125227 100644 --- a/packages/flutter_tts/tizen/src/text_to_speech.cc +++ b/packages/flutter_tts/tizen/src/text_to_speech.cc @@ -30,15 +30,21 @@ void OnError(tts_h tts, int utt_id, tts_error_e reason, void *user_data) { } // namespace -void TextToSpeech::Init() { +void TextToSpeech::Initialize() { + TtsCreate(); + RegisterTtsCallback(); + Prepare(); +} + +void TextToSpeech::TtsCreate() { int ret = tts_create(&tts_); if (ret != TTS_ERROR_NONE) { tts_ = nullptr; - LOG_ERROR("[TTS] tts_create failed: %s", get_error_message(ret)); + throw TextToSpeechError("tts_create", ret); } } -void TextToSpeech::Deinit() { +void TextToSpeech::TtsDestroy() { if (tts_) { tts_destroy(tts_); tts_ = nullptr; @@ -56,15 +62,14 @@ void TextToSpeech::Prepare() { } ret = sound_manager_get_max_volume(SOUND_TYPE_VOICE, &system_max_volume_); if (ret != SOUND_MANAGER_ERROR_NONE) { - LOG_ERROR("[SOUNDMANAGER] sound_manager_get_max_volume failed: %s", - get_error_message(ret)); + throw TextToSpeechError("sound_manager_get_max_volume", ret); } system_volume_ = GetSpeechVolumeInternal(); tts_volume_ = system_volume_; ret = tts_prepare(tts_); if (ret != TTS_ERROR_NONE) { - LOG_ERROR("[TTS] tts_prepare failed: %s", get_error_message(ret)); + throw TextToSpeechError("tts_prepare", ret); } } @@ -109,70 +114,67 @@ std::vector &TextToSpeech::GetSupportedLanaguages() { return supported_lanaguages_; } -tts_state_e TextToSpeech::GetState() { +TtsState TextToSpeech::GetState() { tts_state_e state; int ret = tts_get_state(tts_, &state); if (ret != TTS_ERROR_NONE) { - LOG_ERROR("[TTS] tts_prepare failed: %s", get_error_message(ret)); + throw TextToSpeechError(ret); + } + switch (state) { + case TTS_STATE_CREATED: + return TtsState::kCreated; + case TTS_STATE_READY: + default: + return TtsState::kReady; + case TTS_STATE_PLAYING: + return TtsState::kPlaying; + case TTS_STATE_PAUSED: + return TtsState::kPaused; } - return state; } -bool TextToSpeech::AddText(std::string text) { +void TextToSpeech::AddText(std::string text) { int ret = tts_add_text(tts_, text.c_str(), default_language_.c_str(), default_voice_type_, tts_speed_, &utt_id_); if (ret != TTS_ERROR_NONE) { - LOG_ERROR("[TTS] tts_add_text failed: %s", get_error_message(ret)); - return false; + throw TextToSpeechError("tts_add_text", ret); } - return true; } -bool TextToSpeech::Speak() { +void TextToSpeech::Speak() { int ret = tts_play(tts_); if (ret != TTS_ERROR_NONE) { - LOG_ERROR("[TTS] tts_play failed: %s", get_error_message(ret)); - return false; + throw TextToSpeechError("tts_play", ret); } - return true; } -bool TextToSpeech::Stop() { +void TextToSpeech::Stop() { int ret = tts_stop(tts_); if (ret != TTS_ERROR_NONE) { - LOG_ERROR("[TTS] tts_stop failed: %s", get_error_message(ret)); - return false; + throw TextToSpeechError("tts_stop", ret); } - return true; } -bool TextToSpeech::Pause() { +void TextToSpeech::Pause() { int ret = tts_pause(tts_); if (ret != TTS_ERROR_NONE) { - LOG_ERROR("[TTS] tts_pause failed: %s", get_error_message(ret)); - return false; + throw TextToSpeechError("tts_pause", ret); } - return true; } -bool TextToSpeech::SetVolume(double volume_rate) { +void TextToSpeech::SetVolume(double volume_rate) { tts_volume_ = static_cast(system_max_volume_ * volume_rate); // Change volume instantly when tts is playing. - if (GetState() == TTS_STATE_PLAYING) { - if (!SetSpeechVolumeInternal(tts_volume_)) { - return false; - } + if (GetState() == TtsState::kPlaying) { + SetSpeechVolumeInternal(tts_volume_); } - return true; } -bool TextToSpeech::GetSpeedRange(int *min, int *normal, int *max) { +void TextToSpeech::GetSpeedRange(int *min, int *normal, int *max) { int ret = tts_get_speed_range(tts_, min, normal, max); if (ret != TTS_ERROR_NONE) { - LOG_ERROR("[TTS] tts_get_speed_range failed: %s", get_error_message(ret)); - return false; + throw TextToSpeechError(ret); } - return true; } void TextToSpeech::SwitchVolumeOnStateChange(tts_state_e previous, @@ -194,19 +196,14 @@ int TextToSpeech::GetSpeechVolumeInternal() { int volume; int ret = sound_manager_get_volume(SOUND_TYPE_VOICE, &volume); if (ret != SOUND_MANAGER_ERROR_NONE) { - LOG_ERROR("[SOUNDMANAGER] sound_manager_get_volume failed: %s", - get_error_message(ret)); - volume = 0; + throw TextToSpeechError("sound_manager_get_volume", ret); } return volume; } -bool TextToSpeech::SetSpeechVolumeInternal(int volume) { +void TextToSpeech::SetSpeechVolumeInternal(int volume) { int ret = sound_manager_set_volume(SOUND_TYPE_VOICE, volume); if (ret != SOUND_MANAGER_ERROR_NONE) { - LOG_ERROR("[SOUNDMANAGER] sound_manager_set_volume failed: %s", - get_error_message(ret)); - return false; + throw TextToSpeechError("sound_manager_set_volume", ret); } - return true; } diff --git a/packages/flutter_tts/tizen/src/text_to_speech.h b/packages/flutter_tts/tizen/src/text_to_speech.h index e084a431c..54823d92e 100644 --- a/packages/flutter_tts/tizen/src/text_to_speech.h +++ b/packages/flutter_tts/tizen/src/text_to_speech.h @@ -11,6 +11,27 @@ #include #include +enum class TtsState { kCreated, kReady, kPlaying, kPaused }; + +class TextToSpeechError { + public: + TextToSpeechError(int error_code) : error_code_(error_code) {} + TextToSpeechError(std::string error_message, int error_code) + : error_message_(error_message), error_code_(error_code) {} + + int GetErrorCode() const { return error_code_; } + + std::string GetErrorString() const { + return error_message_.empty() + ? get_error_message(error_code_) + : error_message_ + " - " + get_error_message(error_code_); + } + + private: + int error_code_; + std::string error_message_; +}; + using OnStateChangedCallback = std::function; using OnUtteranceCompletedCallback = std::function; @@ -18,23 +39,15 @@ using OnErrorCallback = std::function; class TextToSpeech { public: - TextToSpeech() { - // TODO : Handle initialization failure cases - // Rarely, initializing TextToSpeech can fail. IMO, in this case, - // we should throw an exception which means that the TextToSpeech instance - // creation failed. In addition, we should consider catching the exception - // and propagating it to the flutter side. however, I think this is optional - // because flutter side is not expecting any errors. - Init(); - RegisterTtsCallback(); - Prepare(); - } + TextToSpeech() = default; ~TextToSpeech() { UnregisterTtsCallback(); - Deinit(); + TtsDestroy(); } + void Initialize(); + void SetOnStateChanagedCallback(OnStateChangedCallback callback) { on_state_changed_ = callback; } @@ -62,27 +75,27 @@ class TextToSpeech { std::vector &GetSupportedLanaguages(); - tts_state_e GetState(); + TtsState GetState(); - bool AddText(std::string text); + void AddText(std::string text); - bool Speak(); + void Speak(); - bool Stop(); + void Stop(); - bool Pause(); + void Pause(); - bool SetVolume(double volume_rate); + void SetVolume(double volume_rate); - bool GetSpeedRange(int *min, int *normal, int *max); + void GetSpeedRange(int *min, int *normal, int *max); void SetTtsSpeed(int speed) { tts_speed_ = speed; } int GetUttId() { return utt_id_; } private: - void Init(); - void Deinit(); + void TtsCreate(); + void TtsDestroy(); void Prepare(); void RegisterTtsCallback(); void UnregisterTtsCallback(); @@ -90,7 +103,7 @@ class TextToSpeech { void ClearUttId() { utt_id_ = 0; } void SwitchVolumeOnStateChange(tts_state_e previous, tts_state_e current); - bool SetSpeechVolumeInternal(int volume); + void SetSpeechVolumeInternal(int volume); int GetSpeechVolumeInternal(); tts_h tts_ = nullptr; From 7db872879eeca2b34c5d50a88d3f9e2d692da71a Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Tue, 21 Jun 2022 15:55:54 +0900 Subject: [PATCH 2/6] Revert c++ exception code --- .../tizen/inc/flutter_tts_tizen_plugin.h | 4 - .../tizen/src/flutter_tts_tizen_plugin.cc | 107 +++++++----------- .../flutter_tts/tizen/src/text_to_speech.cc | 73 ++++++++---- .../flutter_tts/tizen/src/text_to_speech.h | 39 ++----- 4 files changed, 104 insertions(+), 119 deletions(-) diff --git a/packages/flutter_tts/tizen/inc/flutter_tts_tizen_plugin.h b/packages/flutter_tts/tizen/inc/flutter_tts_tizen_plugin.h index 9f62163c6..c89564803 100644 --- a/packages/flutter_tts/tizen/inc/flutter_tts_tizen_plugin.h +++ b/packages/flutter_tts/tizen/inc/flutter_tts_tizen_plugin.h @@ -1,7 +1,3 @@ -// Copyright 2021 Samsung Electronics Co., Ltd. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - #ifndef FLUTTER_PLUGIN_FLUTTER_TTS_TIZEN_PLUGIN_H_ #define FLUTTER_PLUGIN_FLUTTER_TTS_TIZEN_PLUGIN_H_ diff --git a/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc b/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc index 7dd3c8718..a18737e70 100644 --- a/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc +++ b/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc @@ -36,14 +36,11 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { }); tts_ = std::make_unique(); - try { - tts_->Initialize(); - } catch (const TextToSpeechError &error) { + if (!tts_->Initialize()) { // TODO : Handle initialization failure cases // Rarely, initializing TextToSpeech can fail. we should consider catching // the exception and propagating it to the flutter side. however, I think // this is optional because flutter side is not expecting any errors. - LOG_ERROR("Operation failed : %s", error.GetErrorString().c_str()); tts_ = nullptr; return; } @@ -89,6 +86,13 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { void HandleMethodCall( const flutter::MethodCall &method_call, std::unique_ptr> result) { + // Keep in sync with the return values implemented in: + // https://github.com/dlutton/flutter_tts/blob/master/android/src/main/java/com/tundralabs/fluttertts/FlutterTtsPlugin.java. + // In principle, MethodResult was designed to call MethodResult.Error() to + // notify the dart code of any method call failures from the host platform. + // However, in the case of flutter_tts, it expects a return value 0 on + // failure(and value 1 on success). Therefore in the scope of this plugin, + // we call SendResult(flutter::EncodableValue(0)); to notify errors. const auto method_name = method_call.method_name(); const auto &arguments = *method_call.arguments(); @@ -128,62 +132,58 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { SendResult(flutter::EncodableValue(1)); return; } - SendErrorResult("Invalid argument", "Argument is invaild."); + SendResult(flutter::EncodableValue(0)); } void OnSpeak(const flutter::EncodableValue &arguments) { - try { - if (tts_->GetState() == TtsState::kPlaying) { - SendErrorResult("Operation cancelled", - "You cannot speak again while speaking."); - return; + TtsState state = tts_->GetState(); + if (state == TtsState::kPlaying || state == TtsState::kError) { + if (state == TtsState::kPlaying) { + LOG_ERROR("[TTS] : You cannot speak again while speaking."); } + SendResult(flutter::EncodableValue(0)); + return; + } - if (std::holds_alternative(arguments)) { - std::string text = std::get(arguments); - tts_->AddText(text); + if (std::holds_alternative(arguments)) { + std::string text = std::get(arguments); + if (!tts_->AddText(text)) { + SendResult(flutter::EncodableValue(0)); + return; } - - tts_->Speak(); - - } catch (const TextToSpeechError &error) { - SendErrorResult("Operation failed", error.GetErrorString()); } - if (await_speak_completion_ && !result_for_await_speak_completion_) { - LOG_DEBUG("Store result ptr for await speak completion"); - result_for_await_speak_completion_ = std::move(result_); + if (tts_->Speak()) { + if (await_speak_completion_ && !result_for_await_speak_completion_) { + LOG_DEBUG("Store result ptr for await speak completion"); + result_for_await_speak_completion_ = std::move(result_); + } else { + SendResult(flutter::EncodableValue(1)); + } } else { - SendResult(flutter::EncodableValue(1)); + SendResult(flutter::EncodableValue(0)); } } void OnStop() { - try { - tts_->Stop(); - } catch (const TextToSpeechError &error) { - SendErrorResult("Operation failed", error.GetErrorString()); + if (tts_->Stop()) { + SendResult(flutter::EncodableValue(1)); + } else { + SendResult(flutter::EncodableValue(0)); } - SendResult(flutter::EncodableValue(1)); } void OnPause() { - try { - tts_->Pause(); - } catch (const TextToSpeechError &error) { - SendErrorResult("Operation failed", error.GetErrorString()); + if (tts_->Pause()) { + SendResult(flutter::EncodableValue(1)); + } else { + SendResult(flutter::EncodableValue(0)); } - SendResult(flutter::EncodableValue(1)); } void OnGetSpeechRateValidRange() { int min = 0, normal = 0, max = 0; - try { - tts_->GetSpeedRange(&min, &normal, &max); - } catch (const TextToSpeechError &error) { - SendErrorResult("Operation failed", error.GetErrorString()); - return; - } + tts_->GetSpeedRange(&min, &normal, &max); flutter::EncodableMap map; map.insert(std::pair( "min", min)); @@ -203,7 +203,7 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { SendResult(flutter::EncodableValue(1)); return; } - SendErrorResult("Invalid argument", "SpeechRate is invaild."); + SendResult(flutter::EncodableValue(0)); } void OnSetLanguage(const flutter::EncodableValue &arguments) { @@ -213,7 +213,7 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { SendResult(flutter::EncodableValue(1)); return; } - SendErrorResult("Invalid argument", "Language is invaild."); + SendResult(flutter::EncodableValue(0)); } void OnGetLanguage() { @@ -226,15 +226,13 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { void OnSetVolume(const flutter::EncodableValue &arguments) { if (std::holds_alternative(arguments)) { - double volume = std::get(arguments); - try { - tts_->SetVolume(volume); - } catch (const TextToSpeechError &error) { - SendErrorResult("Operation failed", error.GetErrorString()); + double rate = std::get(arguments); + if (tts_->SetVolume(rate)) { + SendResult(flutter::EncodableValue(1)); return; } } - SendErrorResult("Invalid argument", "Volume is invaild."); + SendResult(flutter::EncodableValue(0)); } void SendResult(const flutter::EncodableValue &result) { @@ -245,23 +243,6 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { result_ = nullptr; } - void SendErrorResult(const std::string &error_code, - const std::string &error_message) { - // Keep in sync with the return values implemented in: - // https://github.com/dlutton/flutter_tts/blob/master/android/src/main/java/com/tundralabs/fluttertts/FlutterTtsPlugin.java. - // In principle, MethodResult was designed to call MethodResult.Error() to - // notify the dart code of any method call failures from the host platform. - // However, in the case of flutter_tts, it expects a return value 0 on - // failure(and value 1 on success). Therefore in the scope of this plugin, - // we call result_->Success(flutter::EncodableValue(0)) to notify errors. - if (!result_) { - return; - } - LOG_ERROR("%s", std::string(error_code + " : " + error_message).c_str()); - result_->Success(flutter::EncodableValue(0)); - result_ = nullptr; - } - void HandleAwaitSpeakCompletion(int value) { if (await_speak_completion_) { LOG_DEBUG("Send result for await speak completion[%d]", value); diff --git a/packages/flutter_tts/tizen/src/text_to_speech.cc b/packages/flutter_tts/tizen/src/text_to_speech.cc index 9b0125227..b39d9a3b3 100644 --- a/packages/flutter_tts/tizen/src/text_to_speech.cc +++ b/packages/flutter_tts/tizen/src/text_to_speech.cc @@ -30,18 +30,24 @@ void OnError(tts_h tts, int utt_id, tts_error_e reason, void *user_data) { } // namespace -void TextToSpeech::Initialize() { - TtsCreate(); +bool TextToSpeech::Initialize() { + if (!TtsCreate()) { + return false; + } RegisterTtsCallback(); Prepare(); + + return true; } -void TextToSpeech::TtsCreate() { +bool TextToSpeech::TtsCreate() { int ret = tts_create(&tts_); if (ret != TTS_ERROR_NONE) { + LOG_ERROR("[TTS] tts_create failed: %s", get_error_message(ret)); tts_ = nullptr; - throw TextToSpeechError("tts_create", ret); + return false; } + return true; } void TextToSpeech::TtsDestroy() { @@ -62,14 +68,15 @@ void TextToSpeech::Prepare() { } ret = sound_manager_get_max_volume(SOUND_TYPE_VOICE, &system_max_volume_); if (ret != SOUND_MANAGER_ERROR_NONE) { - throw TextToSpeechError("sound_manager_get_max_volume", ret); + LOG_ERROR("[SOUNDMANAGER] sound_manager_get_max_volume failed: %s", + get_error_message(ret)); } system_volume_ = GetSpeechVolumeInternal(); tts_volume_ = system_volume_; ret = tts_prepare(tts_); if (ret != TTS_ERROR_NONE) { - throw TextToSpeechError("tts_prepare", ret); + LOG_ERROR("[TTS] tts_prepare failed: %s", get_error_message(ret)); } } @@ -118,63 +125,78 @@ TtsState TextToSpeech::GetState() { tts_state_e state; int ret = tts_get_state(tts_, &state); if (ret != TTS_ERROR_NONE) { - throw TextToSpeechError(ret); + LOG_ERROR("[TTS] tts_get_state failed: %s", get_error_message(ret)); + return TtsState::kError; } switch (state) { case TTS_STATE_CREATED: return TtsState::kCreated; case TTS_STATE_READY: - default: return TtsState::kReady; case TTS_STATE_PLAYING: return TtsState::kPlaying; case TTS_STATE_PAUSED: return TtsState::kPaused; + default: + return TtsState::kError; } } -void TextToSpeech::AddText(std::string text) { +bool TextToSpeech::AddText(std::string text) { int ret = tts_add_text(tts_, text.c_str(), default_language_.c_str(), default_voice_type_, tts_speed_, &utt_id_); if (ret != TTS_ERROR_NONE) { - throw TextToSpeechError("tts_add_text", ret); + LOG_ERROR("[TTS] tts_add_text failed: %s", get_error_message(ret)); + return false; } + return true; } -void TextToSpeech::Speak() { +bool TextToSpeech::Speak() { int ret = tts_play(tts_); if (ret != TTS_ERROR_NONE) { - throw TextToSpeechError("tts_play", ret); + LOG_ERROR("[TTS] tts_play failed: %s", get_error_message(ret)); + return false; } + return true; } -void TextToSpeech::Stop() { +bool TextToSpeech::Stop() { int ret = tts_stop(tts_); if (ret != TTS_ERROR_NONE) { - throw TextToSpeechError("tts_stop", ret); + LOG_ERROR("[TTS] tts_stop failed: %s", get_error_message(ret)); + return false; } + return true; } -void TextToSpeech::Pause() { +bool TextToSpeech::Pause() { int ret = tts_pause(tts_); if (ret != TTS_ERROR_NONE) { - throw TextToSpeechError("tts_pause", ret); + LOG_ERROR("[TTS] tts_pause failed: %s", get_error_message(ret)); + return false; } + return true; } -void TextToSpeech::SetVolume(double volume_rate) { +bool TextToSpeech::SetVolume(double volume_rate) { tts_volume_ = static_cast(system_max_volume_ * volume_rate); // Change volume instantly when tts is playing. if (GetState() == TtsState::kPlaying) { - SetSpeechVolumeInternal(tts_volume_); + if (!SetSpeechVolumeInternal(tts_volume_)) { + return false; + } } + return true; } -void TextToSpeech::GetSpeedRange(int *min, int *normal, int *max) { +bool TextToSpeech::GetSpeedRange(int *min, int *normal, int *max) { int ret = tts_get_speed_range(tts_, min, normal, max); if (ret != TTS_ERROR_NONE) { - throw TextToSpeechError(ret); + LOG_ERROR("[TTS] tts_get_speed_range failed: %s", get_error_message(ret)); + return false; } + return true; } void TextToSpeech::SwitchVolumeOnStateChange(tts_state_e previous, @@ -196,14 +218,19 @@ int TextToSpeech::GetSpeechVolumeInternal() { int volume; int ret = sound_manager_get_volume(SOUND_TYPE_VOICE, &volume); if (ret != SOUND_MANAGER_ERROR_NONE) { - throw TextToSpeechError("sound_manager_get_volume", ret); + LOG_ERROR("[SOUNDMANAGER] sound_manager_get_volume failed: %s", + get_error_message(ret)); + volume = 0; } return volume; } -void TextToSpeech::SetSpeechVolumeInternal(int volume) { +bool TextToSpeech::SetSpeechVolumeInternal(int volume) { int ret = sound_manager_set_volume(SOUND_TYPE_VOICE, volume); if (ret != SOUND_MANAGER_ERROR_NONE) { - throw TextToSpeechError("sound_manager_set_volume", ret); + LOG_ERROR("[SOUNDMANAGER] sound_manager_set_volume failed: %s", + get_error_message(ret)); + return false; } + return true; } diff --git a/packages/flutter_tts/tizen/src/text_to_speech.h b/packages/flutter_tts/tizen/src/text_to_speech.h index 54823d92e..6e891bc65 100644 --- a/packages/flutter_tts/tizen/src/text_to_speech.h +++ b/packages/flutter_tts/tizen/src/text_to_speech.h @@ -11,26 +11,7 @@ #include #include -enum class TtsState { kCreated, kReady, kPlaying, kPaused }; - -class TextToSpeechError { - public: - TextToSpeechError(int error_code) : error_code_(error_code) {} - TextToSpeechError(std::string error_message, int error_code) - : error_message_(error_message), error_code_(error_code) {} - - int GetErrorCode() const { return error_code_; } - - std::string GetErrorString() const { - return error_message_.empty() - ? get_error_message(error_code_) - : error_message_ + " - " + get_error_message(error_code_); - } - - private: - int error_code_; - std::string error_message_; -}; +enum class TtsState { kCreated, kReady, kPlaying, kPaused, kError }; using OnStateChangedCallback = std::function; @@ -46,7 +27,7 @@ class TextToSpeech { TtsDestroy(); } - void Initialize(); + bool Initialize(); void SetOnStateChanagedCallback(OnStateChangedCallback callback) { on_state_changed_ = callback; @@ -77,24 +58,24 @@ class TextToSpeech { TtsState GetState(); - void AddText(std::string text); + bool AddText(std::string text); - void Speak(); + bool Speak(); - void Stop(); + bool Stop(); - void Pause(); + bool Pause(); - void SetVolume(double volume_rate); + bool SetVolume(double volume_rate); - void GetSpeedRange(int *min, int *normal, int *max); + bool GetSpeedRange(int *min, int *normal, int *max); void SetTtsSpeed(int speed) { tts_speed_ = speed; } int GetUttId() { return utt_id_; } private: - void TtsCreate(); + bool TtsCreate(); void TtsDestroy(); void Prepare(); void RegisterTtsCallback(); @@ -103,7 +84,7 @@ class TextToSpeech { void ClearUttId() { utt_id_ = 0; } void SwitchVolumeOnStateChange(tts_state_e previous, tts_state_e current); - void SetSpeechVolumeInternal(int volume); + bool SetSpeechVolumeInternal(int volume); int GetSpeechVolumeInternal(); tts_h tts_ = nullptr; From 821edcdd90a2da51aacf397828cce2a5c7960e75 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 24 Jun 2022 16:28:15 +0900 Subject: [PATCH 3/6] Change GetState() return type to use std::optional --- packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc | 7 ++++--- packages/flutter_tts/tizen/src/text_to_speech.cc | 6 +++--- packages/flutter_tts/tizen/src/text_to_speech.h | 5 +++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc b/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc index a18737e70..e6eeb218f 100644 --- a/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc +++ b/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -136,9 +137,9 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { } void OnSpeak(const flutter::EncodableValue &arguments) { - TtsState state = tts_->GetState(); - if (state == TtsState::kPlaying || state == TtsState::kError) { - if (state == TtsState::kPlaying) { + std::optional state = tts_->GetState(); + if (!state.has_value() || state == TtsState::kPlaying) { + if (state.has_value() && state == TtsState::kPlaying) { LOG_ERROR("[TTS] : You cannot speak again while speaking."); } SendResult(flutter::EncodableValue(0)); diff --git a/packages/flutter_tts/tizen/src/text_to_speech.cc b/packages/flutter_tts/tizen/src/text_to_speech.cc index b39d9a3b3..5533ea902 100644 --- a/packages/flutter_tts/tizen/src/text_to_speech.cc +++ b/packages/flutter_tts/tizen/src/text_to_speech.cc @@ -121,12 +121,12 @@ std::vector &TextToSpeech::GetSupportedLanaguages() { return supported_lanaguages_; } -TtsState TextToSpeech::GetState() { +std::optional TextToSpeech::GetState() { tts_state_e state; int ret = tts_get_state(tts_, &state); if (ret != TTS_ERROR_NONE) { LOG_ERROR("[TTS] tts_get_state failed: %s", get_error_message(ret)); - return TtsState::kError; + return std::nullopt; } switch (state) { case TTS_STATE_CREATED: @@ -138,7 +138,7 @@ TtsState TextToSpeech::GetState() { case TTS_STATE_PAUSED: return TtsState::kPaused; default: - return TtsState::kError; + return std::nullopt; } } diff --git a/packages/flutter_tts/tizen/src/text_to_speech.h b/packages/flutter_tts/tizen/src/text_to_speech.h index 6e891bc65..ca9e5e4e2 100644 --- a/packages/flutter_tts/tizen/src/text_to_speech.h +++ b/packages/flutter_tts/tizen/src/text_to_speech.h @@ -8,10 +8,11 @@ #include #include +#include #include #include -enum class TtsState { kCreated, kReady, kPlaying, kPaused, kError }; +enum class TtsState { kCreated, kReady, kPlaying, kPaused }; using OnStateChangedCallback = std::function; @@ -56,7 +57,7 @@ class TextToSpeech { std::vector &GetSupportedLanaguages(); - TtsState GetState(); + std::optional GetState(); bool AddText(std::string text); From 2fc5b2b6ad164a4e764a631c7764dc62800398b9 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Mon, 4 Jul 2022 13:48:20 +0900 Subject: [PATCH 4/6] clean up c++ code - int -> int32_t - remove debug log and [TTS] tag - rename callback functions - remove create and destroy function - etc --- .../tizen/src/flutter_tts_tizen_plugin.cc | 34 ++--- .../flutter_tts/tizen/src/text_to_speech.cc | 135 ++++++++---------- .../flutter_tts/tizen/src/text_to_speech.h | 68 ++++----- 3 files changed, 110 insertions(+), 127 deletions(-) diff --git a/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc b/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc index e6eeb218f..1711a34b2 100644 --- a/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc +++ b/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc @@ -45,7 +45,7 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { tts_ = nullptr; return; } - tts_->SetOnStateChanagedCallback( + tts_->SetStateChanagedCallback( [this](tts_state_e previous, tts_state_e current) -> void { std::unique_ptr value = std::make_unique(true); @@ -68,15 +68,14 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { } }); - tts_->SetOnUtteranceCompletedCallback([this](int utt_id) -> void { - LOG_INFO("[TTS] Utterance (%d) is completed", utt_id); + tts_->SetUtteranceCompletedCallback([this](int32_t utt_id) -> void { std::unique_ptr args = std::make_unique(true); channel_->InvokeMethod("speak.onComplete", std::move(args)); HandleAwaitSpeakCompletion(1); }); - tts_->SetErrorCallback([](int utt_id, tts_error_e reason) -> void { + tts_->SetErrorCallback([](int32_t utt_id, tts_error_e reason) -> void { // It seems unnecessary for now. }); } @@ -140,7 +139,7 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { std::optional state = tts_->GetState(); if (!state.has_value() || state == TtsState::kPlaying) { if (state.has_value() && state == TtsState::kPlaying) { - LOG_ERROR("[TTS] : You cannot speak again while speaking."); + LOG_ERROR("You cannot speak again while speaking."); } SendResult(flutter::EncodableValue(0)); return; @@ -156,7 +155,6 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { if (tts_->Speak()) { if (await_speak_completion_ && !result_for_await_speak_completion_) { - LOG_DEBUG("Store result ptr for await speak completion"); result_for_await_speak_completion_ = std::move(result_); } else { SendResult(flutter::EncodableValue(1)); @@ -183,23 +181,20 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { } void OnGetSpeechRateValidRange() { - int min = 0, normal = 0, max = 0; + int32_t min = 0, normal = 0, max = 0; tts_->GetSpeedRange(&min, &normal, &max); - flutter::EncodableMap map; - map.insert(std::pair( - "min", min)); - map.insert(std::pair( - "normal", normal)); - map.insert(std::pair( - "max", max)); - map.insert(std::pair( - "platform", "tizen")); - SendResult(flutter::EncodableValue(std::move(map))); + flutter::EncodableMap map = { + {flutter::EncodableValue("min"), flutter::EncodableValue(min)}, + {flutter::EncodableValue("normal"), flutter::EncodableValue(normal)}, + {flutter::EncodableValue("max"), flutter::EncodableValue(max)}, + {flutter::EncodableValue("platform"), flutter::EncodableValue("tizen")}, + }; + SendResult(flutter::EncodableValue(map)); } void OnSetSpeechRate(const flutter::EncodableValue &arguments) { if (std::holds_alternative(arguments)) { - int speed = (int)std::get(arguments); + int32_t speed = std::get(arguments); tts_->SetTtsSpeed(speed); SendResult(flutter::EncodableValue(1)); return; @@ -244,9 +239,8 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { result_ = nullptr; } - void HandleAwaitSpeakCompletion(int value) { + void HandleAwaitSpeakCompletion(int32_t value) { if (await_speak_completion_) { - LOG_DEBUG("Send result for await speak completion[%d]", value); result_for_await_speak_completion_->Success( flutter::EncodableValue(value)); result_for_await_speak_completion_ = nullptr; diff --git a/packages/flutter_tts/tizen/src/text_to_speech.cc b/packages/flutter_tts/tizen/src/text_to_speech.cc index 5533ea902..858f4ac29 100644 --- a/packages/flutter_tts/tizen/src/text_to_speech.cc +++ b/packages/flutter_tts/tizen/src/text_to_speech.cc @@ -8,58 +8,32 @@ #include "log.h" -namespace { +TextToSpeech::~TextToSpeech() { + UnregisterCallbacks(); -void OnStateChanged(tts_h tts, tts_state_e previous, tts_state_e current, - void *user_data) { - TextToSpeech *self = static_cast(user_data); - self->OnStateChanged(previous, current); -} - -void OnUtteranceCompleted(tts_h tts, int utt_id, void *user_data) { - TextToSpeech *self = static_cast(user_data); - self->OnUtteranceCompleted(utt_id); - // Explicitly call stop method to change the tts state to ready. - self->Stop(); -} - -void OnError(tts_h tts, int utt_id, tts_error_e reason, void *user_data) { - TextToSpeech *self = static_cast(user_data); - self->OnError(utt_id, reason); -} - -} // namespace - -bool TextToSpeech::Initialize() { - if (!TtsCreate()) { - return false; + if (tts_) { + tts_destroy(tts_); + tts_ = nullptr; } - RegisterTtsCallback(); - Prepare(); - - return true; } -bool TextToSpeech::TtsCreate() { +bool TextToSpeech::Initialize() { int ret = tts_create(&tts_); if (ret != TTS_ERROR_NONE) { - LOG_ERROR("[TTS] tts_create failed: %s", get_error_message(ret)); + LOG_ERROR("tts_create failed: %s", get_error_message(ret)); tts_ = nullptr; return false; } - return true; -} -void TextToSpeech::TtsDestroy() { - if (tts_) { - tts_destroy(tts_); - tts_ = nullptr; - } + RegisterCallbacks(); + Prepare(); + + return true; } void TextToSpeech::Prepare() { char *language = nullptr; - int voice_type = 0; + int32_t voice_type = 0; int ret = tts_get_default_voice(tts_, &language, &voice_type); if (ret == TTS_ERROR_NONE) { default_language_ = language; @@ -68,7 +42,7 @@ void TextToSpeech::Prepare() { } ret = sound_manager_get_max_volume(SOUND_TYPE_VOICE, &system_max_volume_); if (ret != SOUND_MANAGER_ERROR_NONE) { - LOG_ERROR("[SOUNDMANAGER] sound_manager_get_max_volume failed: %s", + LOG_ERROR("sound_manager_get_max_volume failed: %s", get_error_message(ret)); } system_volume_ = GetSpeechVolumeInternal(); @@ -76,43 +50,60 @@ void TextToSpeech::Prepare() { ret = tts_prepare(tts_); if (ret != TTS_ERROR_NONE) { - LOG_ERROR("[TTS] tts_prepare failed: %s", get_error_message(ret)); - } -} - -void TextToSpeech::RegisterTtsCallback() { - tts_set_state_changed_cb(tts_, ::OnStateChanged, (void *)this); - tts_set_utterance_completed_cb(tts_, ::OnUtteranceCompleted, (void *)this); - tts_set_error_cb(tts_, ::OnError, (void *)this); -} - -void TextToSpeech::UnregisterTtsCallback() { + LOG_ERROR("tts_prepare failed: %s", get_error_message(ret)); + } +} + +void TextToSpeech::RegisterCallbacks() { + tts_set_state_changed_cb( + tts_, + [](tts_h tts, tts_state_e previous, tts_state_e current, + void *user_data) { + TextToSpeech *self = static_cast(user_data); + self->OnStateChanged(previous, current); + }, + this); + + tts_set_utterance_completed_cb( + tts_, + [](tts_h tts, int32_t utt_id, void *user_data) { + TextToSpeech *self = static_cast(user_data); + self->OnUtteranceCompleted(utt_id); + // Explicitly call stop method to change the tts state to ready. + self->Stop(); + }, + this); + tts_set_error_cb( + tts_, + [](tts_h tts, int32_t utt_id, tts_error_e reason, void *user_data) { + TextToSpeech *self = static_cast(user_data); + self->OnError(utt_id, reason); + }, + this); +} + +void TextToSpeech::UnregisterCallbacks() { tts_unset_state_changed_cb(tts_); tts_unset_utterance_completed_cb(tts_); tts_unset_error_cb(tts_); } -void TextToSpeech::OnStateChanged(tts_state_e previous, tts_state_e current) { - SwitchVolumeOnStateChange(previous, current); - on_state_changed_(previous, current); -} - std::vector &TextToSpeech::GetSupportedLanaguages() { if (supported_lanaguages_.size() == 0) { tts_foreach_supported_voices( tts_, - [](tts_h tts, const char *language, int voice_type, + [](tts_h tts, const char *language, int32_t voice_type, void *user_data) -> bool { if (language == nullptr) { return false; } TextToSpeech *self = static_cast(user_data); self->supported_lanaguages_.push_back(std::string(language)); - LOG_INFO("[TTS] Supported Voices - Language(%s), Type(%d)", language, + LOG_INFO("Supported Voices - Language(%s), Type(%d)", language, voice_type); return true; }, - (void *)this); + this); supported_lanaguages_.erase( unique(supported_lanaguages_.begin(), supported_lanaguages_.end()), @@ -125,7 +116,7 @@ std::optional TextToSpeech::GetState() { tts_state_e state; int ret = tts_get_state(tts_, &state); if (ret != TTS_ERROR_NONE) { - LOG_ERROR("[TTS] tts_get_state failed: %s", get_error_message(ret)); + LOG_ERROR("tts_get_state failed: %s", get_error_message(ret)); return std::nullopt; } switch (state) { @@ -146,7 +137,7 @@ bool TextToSpeech::AddText(std::string text) { int ret = tts_add_text(tts_, text.c_str(), default_language_.c_str(), default_voice_type_, tts_speed_, &utt_id_); if (ret != TTS_ERROR_NONE) { - LOG_ERROR("[TTS] tts_add_text failed: %s", get_error_message(ret)); + LOG_ERROR("tts_add_text failed: %s", get_error_message(ret)); return false; } return true; @@ -155,7 +146,7 @@ bool TextToSpeech::AddText(std::string text) { bool TextToSpeech::Speak() { int ret = tts_play(tts_); if (ret != TTS_ERROR_NONE) { - LOG_ERROR("[TTS] tts_play failed: %s", get_error_message(ret)); + LOG_ERROR("tts_play failed: %s", get_error_message(ret)); return false; } return true; @@ -164,7 +155,7 @@ bool TextToSpeech::Speak() { bool TextToSpeech::Stop() { int ret = tts_stop(tts_); if (ret != TTS_ERROR_NONE) { - LOG_ERROR("[TTS] tts_stop failed: %s", get_error_message(ret)); + LOG_ERROR("tts_stop failed: %s", get_error_message(ret)); return false; } return true; @@ -173,14 +164,14 @@ bool TextToSpeech::Stop() { bool TextToSpeech::Pause() { int ret = tts_pause(tts_); if (ret != TTS_ERROR_NONE) { - LOG_ERROR("[TTS] tts_pause failed: %s", get_error_message(ret)); + LOG_ERROR("tts_pause failed: %s", get_error_message(ret)); return false; } return true; } bool TextToSpeech::SetVolume(double volume_rate) { - tts_volume_ = static_cast(system_max_volume_ * volume_rate); + tts_volume_ = static_cast(system_max_volume_ * volume_rate); // Change volume instantly when tts is playing. if (GetState() == TtsState::kPlaying) { if (!SetSpeechVolumeInternal(tts_volume_)) { @@ -190,10 +181,10 @@ bool TextToSpeech::SetVolume(double volume_rate) { return true; } -bool TextToSpeech::GetSpeedRange(int *min, int *normal, int *max) { +bool TextToSpeech::GetSpeedRange(int32_t *min, int32_t *normal, int32_t *max) { int ret = tts_get_speed_range(tts_, min, normal, max); if (ret != TTS_ERROR_NONE) { - LOG_ERROR("[TTS] tts_get_speed_range failed: %s", get_error_message(ret)); + LOG_ERROR("tts_get_speed_range failed: %s", get_error_message(ret)); return false; } return true; @@ -214,22 +205,20 @@ void TextToSpeech::SwitchVolumeOnStateChange(tts_state_e previous, } } -int TextToSpeech::GetSpeechVolumeInternal() { - int volume; +int32_t TextToSpeech::GetSpeechVolumeInternal() { + int32_t volume; int ret = sound_manager_get_volume(SOUND_TYPE_VOICE, &volume); if (ret != SOUND_MANAGER_ERROR_NONE) { - LOG_ERROR("[SOUNDMANAGER] sound_manager_get_volume failed: %s", - get_error_message(ret)); + LOG_ERROR("sound_manager_get_volume failed: %s", get_error_message(ret)); volume = 0; } return volume; } -bool TextToSpeech::SetSpeechVolumeInternal(int volume) { +bool TextToSpeech::SetSpeechVolumeInternal(int32_t volume) { int ret = sound_manager_set_volume(SOUND_TYPE_VOICE, volume); if (ret != SOUND_MANAGER_ERROR_NONE) { - LOG_ERROR("[SOUNDMANAGER] sound_manager_set_volume failed: %s", - get_error_message(ret)); + LOG_ERROR("sound_manager_set_volume failed: %s", get_error_message(ret)); return false; } return true; diff --git a/packages/flutter_tts/tizen/src/text_to_speech.h b/packages/flutter_tts/tizen/src/text_to_speech.h index ca9e5e4e2..bd27e248a 100644 --- a/packages/flutter_tts/tizen/src/text_to_speech.h +++ b/packages/flutter_tts/tizen/src/text_to_speech.h @@ -14,40 +14,42 @@ enum class TtsState { kCreated, kReady, kPlaying, kPaused }; -using OnStateChangedCallback = +using StateChangedCallback = std::function; -using OnUtteranceCompletedCallback = std::function; -using OnErrorCallback = std::function; +using UtteranceCompletedCallback = std::function; +using ErrorCallback = std::function; class TextToSpeech { public: TextToSpeech() = default; - ~TextToSpeech() { - UnregisterTtsCallback(); - TtsDestroy(); - } + ~TextToSpeech(); bool Initialize(); - void SetOnStateChanagedCallback(OnStateChangedCallback callback) { - on_state_changed_ = callback; + void SetStateChanagedCallback(StateChangedCallback callback) { + state_changed_callback_ = callback; } - void SetOnUtteranceCompletedCallback(OnUtteranceCompletedCallback callback) { - on_utterance_completed_ = callback; + void SetUtteranceCompletedCallback(UtteranceCompletedCallback callback) { + utterance_completed_callback_ = callback; } - void SetErrorCallback(OnErrorCallback callback) { on_error_ = callback; } + void SetErrorCallback(ErrorCallback callback) { error_callback_ = callback; } - void OnStateChanged(tts_state_e previous, tts_state_e current); + void OnStateChanged(tts_state_e previous, tts_state_e current) { + SwitchVolumeOnStateChange(previous, current); + state_changed_callback_(previous, current); + } - void OnUtteranceCompleted(int utt_id) { - on_utterance_completed_(utt_id); + void OnUtteranceCompleted(int32_t utt_id) { + utterance_completed_callback_(utt_id); ClearUttId(); } - void OnError(int utt_id, tts_error_e reason) { on_error_(utt_id, reason); } + void OnError(int32_t utt_id, tts_error_e reason) { + error_callback_(utt_id, reason); + } std::string GetDefaultLanguage() { return default_language_; } @@ -69,38 +71,36 @@ class TextToSpeech { bool SetVolume(double volume_rate); - bool GetSpeedRange(int *min, int *normal, int *max); + bool GetSpeedRange(int32_t *min, int32_t *normal, int32_t *max); - void SetTtsSpeed(int speed) { tts_speed_ = speed; } + void SetTtsSpeed(int32_t speed) { tts_speed_ = speed; } - int GetUttId() { return utt_id_; } + int32_t GetUttId() { return utt_id_; } private: - bool TtsCreate(); - void TtsDestroy(); void Prepare(); - void RegisterTtsCallback(); - void UnregisterTtsCallback(); + void RegisterCallbacks(); + void UnregisterCallbacks(); void HandleAwaitSpeakCompletion(tts_state_e previous, tts_state_e current); void ClearUttId() { utt_id_ = 0; } void SwitchVolumeOnStateChange(tts_state_e previous, tts_state_e current); - bool SetSpeechVolumeInternal(int volume); - int GetSpeechVolumeInternal(); + bool SetSpeechVolumeInternal(int32_t volume); + int32_t GetSpeechVolumeInternal(); tts_h tts_ = nullptr; std::string default_language_; - int default_voice_type_ = TTS_VOICE_TYPE_AUTO; - int tts_speed_ = TTS_SPEED_AUTO; - int utt_id_ = 0; - int tts_volume_ = 0; - int system_volume_ = 0; - int system_max_volume_ = 0; + int32_t default_voice_type_ = TTS_VOICE_TYPE_AUTO; + int32_t tts_speed_ = TTS_SPEED_AUTO; + int32_t utt_id_ = 0; + int32_t tts_volume_ = 0; + int32_t system_volume_ = 0; + int32_t system_max_volume_ = 0; std::vector supported_lanaguages_; - OnStateChangedCallback on_state_changed_; - OnUtteranceCompletedCallback on_utterance_completed_; - OnErrorCallback on_error_; + StateChangedCallback state_changed_callback_; + UtteranceCompletedCallback utterance_completed_callback_; + ErrorCallback error_callback_; }; #endif // FLUTTER_PLUGIN_TEXT_TO_SPEACH_H_ From d6fa2e523e25dbc959b8c2d11b7353f4c780cfe9 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Mon, 4 Jul 2022 16:19:41 +0900 Subject: [PATCH 5/6] Change variable name --- packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc | 4 ++-- packages/flutter_tts/tizen/src/text_to_speech.cc | 5 +++-- packages/flutter_tts/tizen/src/text_to_speech.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc b/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc index 1711a34b2..1f884a505 100644 --- a/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc +++ b/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc @@ -222,8 +222,8 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { void OnSetVolume(const flutter::EncodableValue &arguments) { if (std::holds_alternative(arguments)) { - double rate = std::get(arguments); - if (tts_->SetVolume(rate)) { + double volume = std::get(arguments); + if (tts_->SetVolume(volume)) { SendResult(flutter::EncodableValue(1)); return; } diff --git a/packages/flutter_tts/tizen/src/text_to_speech.cc b/packages/flutter_tts/tizen/src/text_to_speech.cc index 858f4ac29..28ec0e8ba 100644 --- a/packages/flutter_tts/tizen/src/text_to_speech.cc +++ b/packages/flutter_tts/tizen/src/text_to_speech.cc @@ -40,6 +40,7 @@ void TextToSpeech::Prepare() { default_voice_type_ = voice_type; free(language); } + ret = sound_manager_get_max_volume(SOUND_TYPE_VOICE, &system_max_volume_); if (ret != SOUND_MANAGER_ERROR_NONE) { LOG_ERROR("sound_manager_get_max_volume failed: %s", @@ -170,8 +171,8 @@ bool TextToSpeech::Pause() { return true; } -bool TextToSpeech::SetVolume(double volume_rate) { - tts_volume_ = static_cast(system_max_volume_ * volume_rate); +bool TextToSpeech::SetVolume(double volume) { + tts_volume_ = static_cast(system_max_volume_ * volume); // Change volume instantly when tts is playing. if (GetState() == TtsState::kPlaying) { if (!SetSpeechVolumeInternal(tts_volume_)) { diff --git a/packages/flutter_tts/tizen/src/text_to_speech.h b/packages/flutter_tts/tizen/src/text_to_speech.h index bd27e248a..77c3fe0d3 100644 --- a/packages/flutter_tts/tizen/src/text_to_speech.h +++ b/packages/flutter_tts/tizen/src/text_to_speech.h @@ -69,7 +69,7 @@ class TextToSpeech { bool Pause(); - bool SetVolume(double volume_rate); + bool SetVolume(double volume); bool GetSpeedRange(int32_t *min, int32_t *normal, int32_t *max); From 65ada6a13765912debce3a70bbedb141d6d14f67 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Mon, 4 Jul 2022 17:19:26 +0900 Subject: [PATCH 6/6] flutter_tts: OnGetSpeechRateValidRange use android platform instead of tizen The value of "platform" is temporarily set to "android" instead of "tizen". Because it is not declared in TextToSpeechPlatform of TextToSpeech example. --- packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc b/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc index 1f884a505..36d2c6463 100644 --- a/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc +++ b/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc @@ -183,11 +183,15 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { void OnGetSpeechRateValidRange() { int32_t min = 0, normal = 0, max = 0; tts_->GetSpeedRange(&min, &normal, &max); + // FIXME: The value of "platform" is temporarily set to "android" instead of + // "tizen". Because it is not declared in TextToSpeechPlatform of + // TextToSpeech example. flutter::EncodableMap map = { {flutter::EncodableValue("min"), flutter::EncodableValue(min)}, {flutter::EncodableValue("normal"), flutter::EncodableValue(normal)}, {flutter::EncodableValue("max"), flutter::EncodableValue(max)}, - {flutter::EncodableValue("platform"), flutter::EncodableValue("tizen")}, + {flutter::EncodableValue("platform"), + flutter::EncodableValue("android")}, }; SendResult(flutter::EncodableValue(map)); }