From 4fe281d9b9d8825f37ce785eb0f302448b249c06 Mon Sep 17 00:00:00 2001 From: Swift Kim Date: Thu, 4 Aug 2022 15:28:01 +0900 Subject: [PATCH 1/4] Unsupport getSpeechRateValidRange and improve setSpeechRate --- .../tizen/src/flutter_tts_tizen_plugin.cc | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 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 36d2c6463..011e942ed 100644 --- a/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc +++ b/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -111,8 +112,6 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { 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") { @@ -180,25 +179,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("android")}, - }; - SendResult(flutter::EncodableValue(map)); - } - void OnSetSpeechRate(const flutter::EncodableValue &arguments) { if (std::holds_alternative(arguments)) { - int32_t speed = std::get(arguments); + double rate = std::get(arguments); + rate = std::max(rate, 0.0); + rate = std::min(rate, 1.0); + // Scale the value to be between the supported range. + int32_t min = 0, normal = 0, max = 0; + tts_->GetSpeedRange(&min, &normal, &max); + int32_t speed = min + (max - min) * rate; tts_->SetTtsSpeed(speed); SendResult(flutter::EncodableValue(1)); return; From 3f56e13b4d4bc7a55a8388fddfdcbecf2ef9164b Mon Sep 17 00:00:00 2001 From: Swift Kim Date: Thu, 4 Aug 2022 15:44:14 +0900 Subject: [PATCH 2/4] Too much information --- .../tizen/src/flutter_tts_tizen_plugin.cc | 23 ++++--------------- .../flutter_tts/tizen/src/text_to_speech.cc | 1 - 2 files changed, 5 insertions(+), 19 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 011e942ed..dee7f35c9 100644 --- a/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc +++ b/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc @@ -39,10 +39,6 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { tts_ = std::make_unique(); 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. tts_ = nullptr; return; } @@ -68,17 +64,12 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { } } }); - 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([](int32_t utt_id, tts_error_e reason) -> void { - // It seems unnecessary for now. - }); } virtual ~FlutterTtsTizenPlugin() {} @@ -86,21 +77,17 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { private: 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. + std::unique_ptr result) { + // Keep the return values consistent with the Android implementation. + // The Dart side of this plugin expects a return value of 1 on success and + // 0 on failure, but not result->Error() as other plugins normally expect. const auto method_name = method_call.method_name(); const auto &arguments = *method_call.arguments(); result_ = std::move(result); if (!tts_) { - result_->Error("Operation failed", "TTS is invalid."); + result_->Error("Operation failed", "TTS initialization failed."); return; } diff --git a/packages/flutter_tts/tizen/src/text_to_speech.cc b/packages/flutter_tts/tizen/src/text_to_speech.cc index 28ec0e8ba..c9e9d6952 100644 --- a/packages/flutter_tts/tizen/src/text_to_speech.cc +++ b/packages/flutter_tts/tizen/src/text_to_speech.cc @@ -64,7 +64,6 @@ void TextToSpeech::RegisterCallbacks() { self->OnStateChanged(previous, current); }, this); - tts_set_utterance_completed_cb( tts_, [](tts_h tts, int32_t utt_id, void *user_data) { From d89215ae190d3dc516659f77e0c4984890066396 Mon Sep 17 00:00:00 2001 From: Swift Kim Date: Thu, 4 Aug 2022 16:06:28 +0900 Subject: [PATCH 3/4] Remove ErrorCallback and redefine StateChangedCallback --- .../tizen/src/flutter_tts_tizen_plugin.cc | 57 +++++++++---------- .../flutter_tts/tizen/src/text_to_speech.cc | 46 ++++++++------- .../flutter_tts/tizen/src/text_to_speech.h | 26 ++------- 3 files changed, 58 insertions(+), 71 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 dee7f35c9..7f61f89bc 100644 --- a/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc +++ b/packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc @@ -42,33 +42,32 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { tts_ = nullptr; return; } - tts_->SetStateChanagedCallback( - [this](tts_state_e previous, tts_state_e current) -> void { - std::unique_ptr value = - std::make_unique(true); - if (current == TTS_STATE_PLAYING) { - if (previous == TTS_STATE_READY) { - channel_->InvokeMethod("speak.onStart", std::move(value)); - } else if (previous == TTS_STATE_PAUSED) { - channel_->InvokeMethod("speak.onContinue", std::move(value)); - } - } else if (current == TTS_STATE_PAUSED) { - channel_->InvokeMethod("speak.onPause", std::move(value)); - } else if (current == TTS_STATE_READY) { - if (previous == TTS_STATE_PLAYING || previous == TTS_STATE_PAUSED) { - // utterance ID is not zero during speaking and pausing. - if (tts_->GetUttId()) { - channel_->InvokeMethod("speak.onCancel", std::move(value)); - HandleAwaitSpeakCompletion(0); - } - } + tts_->SetStateChanagedCallback([this](TtsState previous, TtsState current) { + std::unique_ptr value = + std::make_unique(true); + if (current == TtsState::kPlaying) { + if (previous == TtsState::kReady) { + channel_->InvokeMethod("speak.onStart", std::move(value)); + } else if (previous == TtsState::kPaused) { + channel_->InvokeMethod("speak.onContinue", std::move(value)); + } + } else if (current == TtsState::kPaused) { + channel_->InvokeMethod("speak.onPause", std::move(value)); + } else if (current == TtsState::kReady) { + if (previous == TtsState::kPlaying || previous == TtsState::kPaused) { + // The utterance ID is not zero while speaking and pausing. + if (tts_->GetUttId()) { + channel_->InvokeMethod("speak.onCancel", std::move(value)); + HandleAwaitSpeakCompletion(flutter::EncodableValue(0)); } - }); - tts_->SetUtteranceCompletedCallback([this](int32_t utt_id) -> void { + } + } + }); + tts_->SetUtteranceCompletedCallback([this](int32_t utt_id) { std::unique_ptr args = std::make_unique(true); channel_->InvokeMethod("speak.onComplete", std::move(args)); - HandleAwaitSpeakCompletion(1); + HandleAwaitSpeakCompletion(flutter::EncodableValue(1)); }); } @@ -212,17 +211,15 @@ class FlutterTtsTizenPlugin : public flutter::Plugin { } void SendResult(const flutter::EncodableValue &result) { - if (!result_) { - return; + if (result_) { + result_->Success(result); + result_ = nullptr; } - result_->Success(result); - result_ = nullptr; } - void HandleAwaitSpeakCompletion(int32_t value) { + void HandleAwaitSpeakCompletion(const flutter::EncodableValue &result) { if (await_speak_completion_) { - result_for_await_speak_completion_->Success( - flutter::EncodableValue(value)); + result_for_await_speak_completion_->Success(result); 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 c9e9d6952..9b6982716 100644 --- a/packages/flutter_tts/tizen/src/text_to_speech.cc +++ b/packages/flutter_tts/tizen/src/text_to_speech.cc @@ -8,6 +8,23 @@ #include "log.h" +namespace { + +TtsState ConvertTtsState(tts_state_e state) { + switch (state) { + case TTS_STATE_CREATED: + return TtsState::kCreated; + case TTS_STATE_READY: + return TtsState::kReady; + case TTS_STATE_PLAYING: + return TtsState::kPlaying; + case TTS_STATE_PAUSED: + return TtsState::kPaused; + } +} + +} // namespace + TextToSpeech::~TextToSpeech() { UnregisterCallbacks(); @@ -61,23 +78,25 @@ void TextToSpeech::RegisterCallbacks() { [](tts_h tts, tts_state_e previous, tts_state_e current, void *user_data) { TextToSpeech *self = static_cast(user_data); - self->OnStateChanged(previous, current); + self->SwitchVolumeOnStateChange(previous, current); + self->state_changed_callback_(ConvertTtsState(previous), + ConvertTtsState(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->utterance_completed_callback_(utt_id); + self->ClearUttId(); + // Explicitly call Stop() 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); + LOG_ERROR("TTS error: utt_id(%d), reason(%d)", utt_id, reason); }, this); } @@ -99,7 +118,7 @@ std::vector &TextToSpeech::GetSupportedLanaguages() { } TextToSpeech *self = static_cast(user_data); self->supported_lanaguages_.push_back(std::string(language)); - LOG_INFO("Supported Voices - Language(%s), Type(%d)", language, + LOG_INFO("Supported voice: language(%s), type(%d)", language, voice_type); return true; }, @@ -119,21 +138,10 @@ std::optional TextToSpeech::GetState() { LOG_ERROR("tts_get_state failed: %s", get_error_message(ret)); return std::nullopt; } - switch (state) { - case TTS_STATE_CREATED: - return TtsState::kCreated; - case TTS_STATE_READY: - return TtsState::kReady; - case TTS_STATE_PLAYING: - return TtsState::kPlaying; - case TTS_STATE_PAUSED: - return TtsState::kPaused; - default: - return std::nullopt; - } + return ConvertTtsState(state); } -bool TextToSpeech::AddText(std::string text) { +bool TextToSpeech::AddText(const 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) { diff --git a/packages/flutter_tts/tizen/src/text_to_speech.h b/packages/flutter_tts/tizen/src/text_to_speech.h index 77c3fe0d3..c67d4ff45 100644 --- a/packages/flutter_tts/tizen/src/text_to_speech.h +++ b/packages/flutter_tts/tizen/src/text_to_speech.h @@ -15,9 +15,8 @@ enum class TtsState { kCreated, kReady, kPlaying, kPaused }; using StateChangedCallback = - std::function; + std::function; using UtteranceCompletedCallback = std::function; -using ErrorCallback = std::function; class TextToSpeech { public: @@ -35,25 +34,9 @@ class TextToSpeech { utterance_completed_callback_ = callback; } - void SetErrorCallback(ErrorCallback callback) { error_callback_ = callback; } - - void OnStateChanged(tts_state_e previous, tts_state_e current) { - SwitchVolumeOnStateChange(previous, current); - state_changed_callback_(previous, current); - } - - void OnUtteranceCompleted(int32_t utt_id) { - utterance_completed_callback_(utt_id); - ClearUttId(); - } - - void OnError(int32_t utt_id, tts_error_e reason) { - error_callback_(utt_id, reason); - } - std::string GetDefaultLanguage() { return default_language_; } - void SetDefaultLanguage(std::string language) { + void SetDefaultLanguage(const std::string &language) { default_language_ = language; } @@ -61,7 +44,7 @@ class TextToSpeech { std::optional GetState(); - bool AddText(std::string text); + bool AddText(const std::string &text); bool Speak(); @@ -81,7 +64,7 @@ class TextToSpeech { void Prepare(); 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); @@ -100,7 +83,6 @@ class TextToSpeech { StateChangedCallback state_changed_callback_; UtteranceCompletedCallback utterance_completed_callback_; - ErrorCallback error_callback_; }; #endif // FLUTTER_PLUGIN_TEXT_TO_SPEACH_H_ From 482aa943d789f58257663b67cfee4f0a0e4c593b Mon Sep 17 00:00:00 2001 From: Swift Kim Date: Thu, 4 Aug 2022 17:06:24 +0900 Subject: [PATCH 4/4] Update miscellaneous --- packages/flutter_tts/CHANGELOG.md | 7 +++++++ packages/flutter_tts/README.md | 6 +++--- packages/flutter_tts/example/lib/main.dart | 11 ++++++++--- packages/flutter_tts/example/pubspec.yaml | 2 +- packages/flutter_tts/pubspec.yaml | 4 ++-- 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/packages/flutter_tts/CHANGELOG.md b/packages/flutter_tts/CHANGELOG.md index a89a57295..c61b38bb8 100644 --- a/packages/flutter_tts/CHANGELOG.md +++ b/packages/flutter_tts/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.3.0 + +* Limit the range of `setSpeechRate` to be between 0.0 and 1.0. +* Unsupport `getSpeechRateValidRange`. +* Update flutter_tts to 3.5.0. +* Update the example app. + ## 1.2.1 * Refactor the C++ code. diff --git a/packages/flutter_tts/README.md b/packages/flutter_tts/README.md index 033bbd9fc..ee9cab00e 100644 --- a/packages/flutter_tts/README.md +++ b/packages/flutter_tts/README.md @@ -2,7 +2,7 @@ [![pub package](https://img.shields.io/pub/v/flutter_tts_tizen.svg)](https://pub.dev/packages/flutter_tts_tizen) -The tizen implementation of [`flutter_tts`](https://github.com/dlutton/flutter_tts). +The Tizen implementation of [`flutter_tts`](https://github.com/dlutton/flutter_tts). ## Getting Started @@ -10,8 +10,8 @@ This package is not an _endorsed_ implementation of `flutter_tts`. Therefore, yo ```yaml dependencies: - flutter_tts: ^3.2.2 - flutter_tts_tizen: ^1.2.0 + flutter_tts: ^3.5.0 + flutter_tts_tizen: ^1.3.0 ``` Then you can import `flutter_tts` in your Dart code: diff --git a/packages/flutter_tts/example/lib/main.dart b/packages/flutter_tts/example/lib/main.dart index 917db5b25..8c2336189 100644 --- a/packages/flutter_tts/example/lib/main.dart +++ b/packages/flutter_tts/example/lib/main.dart @@ -37,6 +37,8 @@ class _MyAppState extends State { initTts() { flutterTts = FlutterTts(); + _setAwaitOptions(); + flutterTts.setStartHandler(() { setState(() { print("Playing"); @@ -88,12 +90,15 @@ class _MyAppState extends State { if (_newVoiceText != null) { if (_newVoiceText!.isNotEmpty) { - await flutterTts.awaitSpeakCompletion(true); await flutterTts.speak(_newVoiceText!); } } } + Future _setAwaitOptions() async { + await flutterTts.awaitSpeakCompletion(true); + } + Future _stop() async { var result = await flutterTts.stop(); if (result == 1) setState(() => ttsState = TtsState.stopped); @@ -241,8 +246,8 @@ class _MyAppState extends State { setState(() => rate = newRate); }, min: 0.0, - max: 15.0, - divisions: 15, + max: 1.0, + divisions: 10, label: "Rate: $rate", activeColor: Colors.green, ); diff --git a/packages/flutter_tts/example/pubspec.yaml b/packages/flutter_tts/example/pubspec.yaml index efcb317f7..c0ad35421 100644 --- a/packages/flutter_tts/example/pubspec.yaml +++ b/packages/flutter_tts/example/pubspec.yaml @@ -5,7 +5,7 @@ publish_to: "none" dependencies: flutter: sdk: flutter - flutter_tts: ^3.2.2 + flutter_tts: ^3.5.0 flutter_tts_tizen: path: ../ diff --git a/packages/flutter_tts/pubspec.yaml b/packages/flutter_tts/pubspec.yaml index 5b443d38e..644092a90 100644 --- a/packages/flutter_tts/pubspec.yaml +++ b/packages/flutter_tts/pubspec.yaml @@ -1,8 +1,8 @@ name: flutter_tts_tizen -description: The tizen implementation of flutter_tts plugin. +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.1 +version: 1.3.0 dependencies: flutter: