Skip to content

[flutter_tts] Refactor the C++ code #401

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/flutter_tts/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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`.
Expand Down
2 changes: 1 addition & 1 deletion packages/flutter_tts/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
264 changes: 164 additions & 100 deletions packages/flutter_tts/tizen/src/flutter_tts_tizen_plugin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@
#include <flutter/plugin_registrar.h>
#include <flutter/standard_method_codec.h>

#include <map>
#include <memory>
#include <sstream>
#include <optional>
#include <string>
#include <vector>

#include "log.h"
#include "text_to_speech.h"

namespace {

typedef flutter::MethodChannel<flutter::EncodableValue> FlMethodChannel;
typedef flutter::MethodResult<flutter::EncodableValue> FlMethodResult;

class FlutterTtsTizenPlugin : public flutter::Plugin {
public:
static void RegisterWithRegistrar(flutter::PluginRegistrar *registrar) {
Expand All @@ -25,16 +29,23 @@ class FlutterTtsTizenPlugin : public flutter::Plugin {
}

FlutterTtsTizenPlugin(flutter::PluginRegistrar *registrar) {
channel_ =
std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
registrar->messenger(), "flutter_tts",
&flutter::StandardMethodCodec::GetInstance());
channel_ = std::make_unique<FlMethodChannel>(
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<TextToSpeech>();
tts_->SetOnStateChanagedCallback(
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;
}
tts_->SetStateChanagedCallback(
[this](tts_state_e previous, tts_state_e current) -> void {
std::unique_ptr<flutter::EncodableValue> value =
std::make_unique<flutter::EncodableValue>(true);
Expand All @@ -57,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<flutter::EncodableValue> args =
std::make_unique<flutter::EncodableValue>(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.
});
}
Expand All @@ -82,121 +92,175 @@ class FlutterTtsTizenPlugin : public flutter::Plugin {
// 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.

// we call SendResult(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<bool>(arguments)) {
await_speak_completion_ = std::get<bool>(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));
return;
}
result_ = std::move(result);

if (std::holds_alternative<std::string>(arguments)) {
std::string text = std::get<std::string>(arguments);
if (!tts_->AddText(text)) {
result->Success(flutter::EncodableValue(0));
return;
}
}
if (!tts_) {
result_->Error("Operation failed", "TTS is invalid.");
return;
}

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));
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<bool>(arguments)) {
await_speak_completion_ = std::get<bool>(arguments);
SendResult(flutter::EncodableValue(1));
return;
}
SendResult(flutter::EncodableValue(0));
}

void OnSpeak(const flutter::EncodableValue &arguments) {
std::optional<TtsState> state = tts_->GetState();
if (!state.has_value() || state == TtsState::kPlaying) {
if (state.has_value() && state == TtsState::kPlaying) {
LOG_ERROR("You cannot speak again while speaking.");
}
} else if (method_name.compare("stop") == 0) {
if (tts_->Stop()) {
result->Success(flutter::EncodableValue(1));
} else {
result->Success(flutter::EncodableValue(0));
SendResult(flutter::EncodableValue(0));
return;
}

if (std::holds_alternative<std::string>(arguments)) {
std::string text = std::get<std::string>(arguments);
if (!tts_->AddText(text)) {
SendResult(flutter::EncodableValue(0));
return;
}
} else if (method_name.compare("pause") == 0) {
if (tts_->Pause()) {
result->Success(flutter::EncodableValue(1));
}

if (tts_->Speak()) {
if (await_speak_completion_ && !result_for_await_speak_completion_) {
result_for_await_speak_completion_ = std::move(result_);
} else {
result->Success(flutter::EncodableValue(0));
}
} else if (method_name.compare("getSpeechRateValidRange") == 0) {
int min = 0, normal = 0, max = 0;
tts_->GetSpeedRange(&min, &normal, &max);
flutter::EncodableMap map;
map.insert(std::pair<flutter::EncodableValue, flutter::EncodableValue>(
"min", min));
map.insert(std::pair<flutter::EncodableValue, flutter::EncodableValue>(
"normal", normal));
map.insert(std::pair<flutter::EncodableValue, flutter::EncodableValue>(
"max", max));
map.insert(std::pair<flutter::EncodableValue, flutter::EncodableValue>(
"platform", "tizen"));
result->Success(flutter::EncodableValue(std::move(map)));
} else if (method_name.compare("setSpeechRate") == 0) {
if (std::holds_alternative<double>(arguments)) {
int speed = (int)std::get<double>(arguments);
tts_->SetTtsSpeed(speed);
result->Success(flutter::EncodableValue(1));
return;
SendResult(flutter::EncodableValue(1));
}
result->Success(flutter::EncodableValue(0));
} else if (method_name.compare("setLanguage") == 0) {
if (std::holds_alternative<std::string>(arguments)) {
std::string language = std::move(std::get<std::string>(arguments));
tts_->SetDefaultLanguage(language);
result->Success(flutter::EncodableValue(1));
} else {
SendResult(flutter::EncodableValue(0));
}
}

void OnStop() {
if (tts_->Stop()) {
SendResult(flutter::EncodableValue(1));
} else {
SendResult(flutter::EncodableValue(0));
}
}

void OnPause() {
if (tts_->Pause()) {
SendResult(flutter::EncodableValue(1));
} else {
SendResult(flutter::EncodableValue(0));
}
}

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<double>(arguments)) {
int32_t speed = std::get<double>(arguments);
tts_->SetTtsSpeed(speed);
SendResult(flutter::EncodableValue(1));
return;
}
SendResult(flutter::EncodableValue(0));
}

void OnSetLanguage(const flutter::EncodableValue &arguments) {
if (std::holds_alternative<std::string>(arguments)) {
std::string language = std::move(std::get<std::string>(arguments));
tts_->SetDefaultLanguage(language);
SendResult(flutter::EncodableValue(1));
return;
}
SendResult(flutter::EncodableValue(0));
}

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<double>(arguments)) {
double volume = std::get<double>(arguments);
if (tts_->SetVolume(volume)) {
SendResult(flutter::EncodableValue(1));
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<double>(arguments)) {
double rate = std::get<double>(arguments);
if (tts_->SetVolume(rate)) {
result->Success(flutter::EncodableValue(1));
return;
}
}
result->Success(flutter::EncodableValue(0));
} else {
result->Error("-1", "Not supported method");
}
SendResult(flutter::EncodableValue(0));
}

void SendResult(const flutter::EncodableValue &result) {
if (!result_) {
return;
}
result_->Success(result);
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;
}
}

std::unique_ptr<TextToSpeech> tts_;
std::unique_ptr<flutter::MethodChannel<flutter::EncodableValue>> channel_;

bool await_speak_completion_ = false;
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>>
result_for_await_speak_completion_;

std::unique_ptr<FlMethodResult> result_for_await_speak_completion_;
std::unique_ptr<FlMethodResult> result_;
std::unique_ptr<FlMethodChannel> channel_;
};

} // namespace

void FlutterTtsTizenPluginRegisterWithRegistrar(
FlutterDesktopPluginRegistrarRef registrar) {
FlutterTtsTizenPlugin::RegisterWithRegistrar(
Expand Down
Loading